axios 소스 코드 시리즈 (2) - 어댑터 내부

28367 단어 node.js자바 script
기본 어댑터
axios/lib/adapters/http.js
핵심 은 nodejs 의 http (s). request 방법 으로 요청 합 니 다.
var utils = require('./../utils');
var settle = require('./../core/settle');
var buildFullPath = require('../core/buildFullPath');
var buildURL = require('./../helpers/buildURL');
var http = require('http');
var https = require('https');
var httpFollow = require('follow-redirects').http;
var httpsFollow = require('follow-redirects').https;
var url = require('url');
var zlib = require('zlib');
var pkg = require('./../../package.json');
var createError = require('../core/createError');
var enhanceError = require('../core/enhanceError');

var isHttps = /https:?/;

우 리 는 먼저 안에 인 용 된 라 이브 러 리 작용 을 살 펴 보 자.
창고.
역할.
http
http 요청 라 이브 러 리
https
https 요청 라 이브 러 리
follow-redirects
nodejs 의 http 와 https 모듈 을 대체 하여 자동 으로 방향 을 바 꿉 니 다.
url
url 분석 및 포맷
zlib
간단, 동기 압축 또는 압축 풀기 node. js buffers.
안에 또 다른 내장 모듈 이 있다.
axios/lib/core/settle.js
var createError = require('./createError');

/**
 * Resolve or reject a Promise based on response status.
 *
 * @param {Function} resolve A function that resolves the promise.
 * @param {Function} reject A function that rejects the promise.
 * @param {object} response The response.
 */
module.exports = function settle(resolve, reject, response) {
  var validateStatus = response.config.validateStatus;
  if (!validateStatus || validateStatus(response.status)) {
    resolve(response);
  } else {
    reject(createError(
      'Request failed with status code ' + response.status,
      response.config,
      null,
      response.request,
      response
    ));
  }
};

응답 요청 을 받 은 상태 에서 성공 하거나 실패 한 Promise 상태 로 돌아 가기 로 결 정 했 습 니 다. 실패 할 때 사용자 정의 오류 가 발생 합 니 다.
axios/lib/core/createError.js
var enhanceError = require('./enhanceError');

/**
 * Create an Error with the specified message, config, error code, request and response.
 *
 * @param {string} message The error message.
 * @param {Object} config The config.
 * @param {string} [code] The error code (for example, 'ECONNABORTED').
 * @param {Object} [request] The request.
 * @param {Object} [response] The response.
 * @returns {Error} The created error.
 */
module.exports = function createError(message, config, code, request, response) {
  var error = new Error(message);
  return enhanceError(error, config, code, request, response);
};

중간 역 의 뜻 이 있 습 니 다. 잘못된 대상 만 만 들 고 수정 한 임 무 는 enhanceError 함수 에 맡 깁 니 다.
axios/lib/core/enhanceError.js
/**
 * Update an Error with the specified config, error code, and response.
 *
 * @param {Error} error The error to update.
 * @param {Object} config The config.
 * @param {string} [code] The error code (for example, 'ECONNABORTED').
 * @param {Object} [request] The request.
 * @param {Object} [response] The response.
 * @returns {Error} The error.
 */
module.exports = function enhanceError(error, config, code, request, response) {
  error.config = config;
  if (code) {
    error.code = code;
  }

  error.request = request;
  error.response = response;
  error.isAxiosError = true;

  error.toJSON = function() {
    return {
      // Standard
      message: this.message,
      name: this.name,
      // Microsoft
      description: this.description,
      number: this.number,
      // Mozilla
      fileName: this.fileName,
      lineNumber: this.lineNumber,
      columnNumber: this.columnNumber,
      stack: this.stack,
      // Axios
      config: this.config,
      code: this.code
    };
  };
  return error;
};

포장 에 들 어 온 오류 대상 을 되 돌려 주 고 가능 한 한 많은 정 보 를 오류 대상 에 할당 합 니 다. 그 중에서 일부 브 라 우 저가 제공 한 오류 정 보 를 포함 합 니 다.
/*eslint consistent-return:0*/
module.exports = function httpAdapter(config) {
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
    var resolve = function resolve(value) {
      resolvePromise(value);
    };
    var reject = function reject(value) {
      rejectPromise(value);
    };
    var data = config.data;
    var headers = config.headers;

    // Set User-Agent (required by some servers)
    // Only set header if it hasn't been set in config
    // See https://github.com/axios/axios/issues/69
    if (!headers['User-Agent'] && !headers['user-agent']) {
      headers['User-Agent'] = 'axios/' + pkg.version;
    }
    // ---      ---
  });
};

요청 한 설정 정 보 를 입력 하고 새로운 Promise 를 되 돌려 주 며 상 태 를 바 꾸 는 트리거 함 수 를 변수 에 할당 합 니 다. 요청 헤더 에 User-Agent 또는 user-agent 가 포함 되 지 않 을 때 기본 값 axios 의 특유 한 정보 입 니 다. pkg 는 axios package.json 입 니 다.
/*eslint consistent-return:0*/
module.exports = function httpAdapter(config) {
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
    // ---      ---
    
    if (data && !utils.isStream(data)) {
      if (Buffer.isBuffer(data)) {
        // Nothing to do...
      } else if (utils.isArrayBuffer(data)) {
        data = Buffer.from(new Uint8Array(data));
      } else if (utils.isString(data)) {
        data = Buffer.from(data, 'utf-8');
      } else {
        return reject(createError(
          'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream',
          config
        ));
      }

      // Add Content-Length header if data exists
      headers['Content-Length'] = data.length;
    }
    
    // ---      ---
  });
};

data 데이터 가 있 고 흐름 이 아 닌 경우 data 의 데이터 형식 에 따라 대응 하 는 변환 을 하고 Content-Length 의 길 이 를 data 로 설정 합 니 다. 모두 부합 되 지 않 는 상황 에서 오류 상태 Promise 를 되 돌려 주 고 절 차 를 중단 합 니 다.
/*eslint consistent-return:0*/
module.exports = function httpAdapter(config) {
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
    // ---      ---
    
    // HTTP basic authentication
    var auth = undefined;
    if (config.auth) {
      var username = config.auth.username || '';
      var password = config.auth.password || '';
      auth = username + ':' + password;
    }

    // Parse url
    var fullPath = buildFullPath(config.baseURL, config.url);
    var parsed = url.parse(fullPath);
    var protocol = parsed.protocol || 'http:';

    if (!auth && parsed.auth) {
      var urlAuth = parsed.auth.split(':');
      var urlUsername = urlAuth[0] || '';
      var urlPassword = urlAuth[1] || '';
      auth = urlUsername + ':' + urlPassword;
    }

    if (auth) {
      delete headers.Authorization;
    }
    
    var isHttpsRequest = isHttps.test(protocol);
    var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;

    var options = {
      path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
      method: config.method.toUpperCase(),
      headers: headers,
      agent: agent,
      agents: {
        httpsAgent: config.httpsAgent,
        httpAgent: config.httpAgent
      },
      auth: auth
    };
    
    if (config.socketPath) {
      options.socketPath = config.socketPath;
    } else {
      options.hostname = parsed.hostname;
      options.port = parsed.port;
    }
    
    // ---      ---
  });
};
  • 포함 auth 을 설정 하면 사용자 이름과 비밀 번 호 를 연결 하여 비 울 수 있 습 니 다.
  • 완전한 요청 주소 와 요청 프로 토 콜 을 조합 합 니 다.
  • 설정 에 포함 되 지 않 고 요청 주소 에 호 환 처 리 를 해 야 하 는 경우 가 있 습 니 다.
  • 만약 auth 정보 가 있 으 면 Authorization 헤 더 를 삭제 합 니 다. 즉, '사용자 이름 + 콜론 + 비밀번호' 가 BASE 64 알고리즘 으로 암호 화 된 문자열 입 니 다.
  • https 프로 토 콜 에서 설정 한 httpsAgent 정 보 를 얻 지 않 으 면 httpAgent 정 보 를 가 져 옵 니 다.
  • 모든 정 보 를 options 에 넣는다.
  • 경로 또는 sockethostname 를 설정 합 니 다.

  • 일반적인 검증 절차
    HTTP Authorization 요청 레이 블 은 서버 에 사용자 프 록 시 를 인증 하 는 데 사용 되 는 증명 서 를 포함 하고 있 으 며, 일반적으로 서버 가 401 Unauthorized 상태 와 WWW - Authenticate 제목 에 응답 한 후 입 니 다.
    서버 가 요청 을 받 았 을 때 인증 이 필요 한 정보 가 설정 되 어 있 으 며, 요청 머리띠 에 Authorization 이 있 으 면 그 내용 이 사용자 목록 에 있 는 지 확인 합 니 다.
    요청 한 머리띠 port 가 있 으 면 사용자 목록 에 있 는 지 확인 합 니 다.
  • 있 고 검증 이 통과 되면 정상 적 인 응답 으로 돌아 갑 니 다
  • 그렇지 않 으 면 Authorization 상태 코드 로 돌아 갑 니 다. 브 라 우 저 는 대화 상 자 를 꺼 내 계 정 비밀 번 호 를 입력 하 게 합 니 다
  • 위 에서 또 몇 가지 방법 을 언급 하 였 는데, 각각
    axios/lib/core/buildFullPath.js
    var isAbsoluteURL = require('../helpers/isAbsoluteURL');
    var combineURLs = require('../helpers/combineURLs');
    
    /**
     * Creates a new URL by combining the baseURL with the requestedURL,
     * only when the requestedURL is not already an absolute URL.
     * If the requestURL is absolute, this function returns the requestedURL untouched.
     *
     * @param {string} baseURL The base URL
     * @param {string} requestedURL Absolute or relative URL to combine
     * @returns {string} The combined full path
     */
    module.exports = function buildFullPath(baseURL, requestedURL) {
      if (baseURL && !isAbsoluteURL(requestedURL)) {
        return combineURLs(baseURL, requestedURL);
      }
      return requestedURL;
    };

    완전 하고 규범 화 된 절대 주 소 를 되 돌려 줍 니 다.
    axios/lib/helpers/isAbsoluteURL.js
    /**
     * Determines whether the specified URL is absolute
     *
     * @param {string} url The URL to test
     * @returns {boolean} True if the specified URL is absolute, otherwise false
     */
    module.exports = function isAbsoluteURL(url) {
      // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL).
      // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
      // by any combination of letters, digits, plus, period, or hyphen.
      return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
    };

    절대 경로 여 부 를 정규 적 으로 판단 하 다.
    axios/lib/helpers/combineURLs.js
    /**
     * Creates a new URL by combining the specified URLs
     *
     * @param {string} baseURL The base URL
     * @param {string} relativeURL The relative URL
     * @returns {string} The combined URL
     */
    module.exports = function combineURLs(baseURL, relativeURL) {
      return relativeURL
        ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
        : baseURL;
    };

    병합 주 소 를 맞 추고 규범화 합 니 다.
    axios/lib/helpers/buildURL.js
    var utils = require('./../utils');
    
    function encode(val) {
      return encodeURIComponent(val).
        replace(/%40/gi, '@').
        replace(/%3A/gi, ':').
        replace(/%24/g, '$').
        replace(/%2C/gi, ',').
        replace(/%20/g, '+').
        replace(/%5B/gi, '[').
        replace(/%5D/gi, ']');
    }
    
    /**
     * Build a URL by appending params to the end
     *
     * @param {string} url The base of the url (e.g., http://www.google.com)
     * @param {object} [params] The params to be appended
     * @returns {string} The formatted url
     */
    module.exports = function buildURL(url, params, paramsSerializer) {
      /*eslint no-param-reassign:0*/
      if (!params) {
        return url;
      }
    
      var serializedParams;
      if (paramsSerializer) {
        serializedParams = paramsSerializer(params);
      } else if (utils.isURLSearchParams(params)) {
        serializedParams = params.toString();
      } else {
        var parts = [];
    
        utils.forEach(params, function serialize(val, key) {
          if (val === null || typeof val === 'undefined') {
            return;
          }
    
          if (utils.isArray(val)) {
            key = key + '[]';
          } else {
            val = [val];
          }
    
          utils.forEach(val, function parseValue(v) {
            if (utils.isDate(v)) {
              v = v.toISOString();
            } else if (utils.isObject(v)) {
              v = JSON.stringify(v);
            }
            parts.push(encode(key) + '=' + encode(v));
          });
        });
    
        serializedParams = parts.join('&');
      }
    
      if (serializedParams) {
        var hashmarkIndex = url.indexOf('#');
        if (hashmarkIndex !== -1) {
          url = url.slice(0, hashmarkIndex);
        }
    
        url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
      }
    
      return url;
    };

    우 리 는 도구 모듈 을 도입 한 후에 자체 모듈 이 하나의 401 방법 을 사용자 정의 한 것 을 볼 수 있다.encode 안에 또 하나의 buildURL 입 참 이 있 는데 주석 에서 보지 못 했 지만 매개 변수 서열 화 방법 이 라 고 추측 할 수 있다.
    첫 번 째 설정 paramsSerializer 의 절차 에는 세 가지 조건 이 있 습 니 다.
  • 방법 이 있 으 면 직접 처리 serializedParams 매개 변수
  • paramsSerializer 대상 이면 직접 호출 params 방법
  • 그렇지 않 으 면 직접 호출 params 하고 유형 에 따라 반전 함수 에서 한 층 으로 전환 하여 최종 적 으로 URLSearchParams 맞 춤 형 문자열 매개 변수
  • 를 출력 합 니 다.
    두 번 째 단 계 는 위 에서 toString 를 얻 을 수 있 는 경우 url 규칙 에 따라 연결 합 니 다.
    세 번 째 단 계 는 맞 춤 형 url 또는 원본 url 로 되 돌아 갑 니 다.
    다음 소스 보 겠 습 니 다.
    /*eslint consistent-return:0*/
    module.exports = function httpAdapter(config) {
      return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
        // ---      ---
        
        var proxy = config.proxy;
        //                  
        if (!proxy && proxy !== false) {
          //          ,          
          var proxyEnv = protocol.slice(0, -1) + '_proxy';
          //     
          var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()];
          if (proxyUrl) {
            //       
            var parsedProxyUrl = url.parse(proxyUrl);
            // no_proxy    
            var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY;
            var shouldProxy = true;
    
            if (noProxyEnv) {
              //               
              var noProxy = noProxyEnv.split(',').map(function trim(s) {
                return s.trim();
              });
              //       
              shouldProxy = !noProxy.some(function proxyMatch(proxyElement) {
                //      false
                if (!proxyElement) {
                  return false;
                }
                //      true
                if (proxyElement === '*') {
                  return true;
                }
                //   proxyElement   url       
                if (proxyElement[0] === '.' &&
                    parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) {
                  return true;
                }
    
                return parsed.hostname === proxyElement;
              });
            }
    
            //       
            if (shouldProxy) {
              proxy = {
                host: parsedProxyUrl.hostname,
                port: parsedProxyUrl.port
              };
    
              if (parsedProxyUrl.auth) {
                var proxyUrlAuth = parsedProxyUrl.auth.split(':');
                proxy.auth = {
                  username: proxyUrlAuth[0],
                  password: proxyUrlAuth[1]
                };
              }
            }
          }
        }
    
        //        ,   options
        if (proxy) {
          options.hostname = proxy.host;
          options.host = proxy.host;
          options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : '');
          options.port = proxy.port;
          options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path;
    
          // Basic proxy authorization
          if (proxy.auth) {
            var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64');
            options.headers['Proxy-Authorization'] = 'Basic ' + base64;
          }
        }
        
        // ---      ---
      });
    };

    이 코드 는 정의 프 록 시 서버 에 속 합 니 다. 예 는 다음 과 같 습 니 다.
    proxy: {
        host: '127.0.0.1',
        port: 9000,
        auth: {
            username: 'mikeymike',
            password: 'rapunz3l'
        }
    },

    구체 적 으로 무엇 을 했 는 지 한눈 에 알 수 있다.
    /*eslint consistent-return:0*/
    module.exports = function httpAdapter(config) {
      return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
        // ---      ---
        
        var transport;
        //   https  
        var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true);
        //          
        if (config.transport) {
          transport = config.transport;
        } else if (config.maxRedirects === 0) {
          //         0    https    http  
          transport = isHttpsProxy ? https : http;
        } else {
          //        
          if (config.maxRedirects) {
            options.maxRedirects = config.maxRedirects;
          }
          //       https       http     
          transport = isHttpsProxy ? httpsFollow : httpFollow;
        }
        //            -1    options 
        if (config.maxContentLength && config.maxContentLength > -1) {
          options.maxBodyLength = config.maxContentLength;
        }
        
        // ---      ---
      });
    };

    프로 토 콜 에 따라 해당 하 는 요청 라 이브 러 리 를 사용 하고 최대 리 셋 횟수 와 요청 내용 의 길 이 를 설정 합 니 다.
    /*eslint consistent-return:0*/
    module.exports = function httpAdapter(config) {
      return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
        // ---      ---
        
        // Create the request
        var req = transport.request(options, function handleResponse(res) {
          //          
          if (req.aborted) return;
    
          // uncompress the response body transparently if required
          var stream = res;
          switch (res.headers['content-encoding']) {
          /*eslint default-case:0*/
          case 'gzip':
          case 'compress':
          case 'deflate':
            // add the unzipper to the body stream processing pipeline
            stream = (res.statusCode === 204) ? stream : stream.pipe(zlib.createUnzip());
    
            // remove the content-encoding in order to not confuse downstream operations
            delete res.headers['content-encoding'];
            break;
          }
    
          // return the last request in case of redirects
          var lastRequest = res.req || req;
    
          var response = {
            status: res.statusCode,
            statusText: res.statusMessage,
            headers: res.headers,
            config: config,
            request: lastRequest
          };
    
          if (config.responseType === 'stream') {
            response.data = stream;
            settle(resolve, reject, response);
          } else {
            var responseBuffer = [];
            stream.on('data', function handleStreamData(chunk) {
              responseBuffer.push(chunk);
    
              // make sure the content length is not over the maxContentLength if specified
              if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
                stream.destroy();
                reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
                  config, null, lastRequest));
              }
            });
    
            stream.on('error', function handleStreamError(err) {
              if (req.aborted) return;
              reject(enhanceError(err, config, null, lastRequest));
            });
    
            stream.on('end', function handleStreamEnd() {
              var responseData = Buffer.concat(responseBuffer);
              if (config.responseType !== 'arraybuffer') {
                responseData = responseData.toString(config.responseEncoding);
              }
    
              response.data = responseData;
              settle(resolve, reject, response);
            });
          }
        });
        
        // ---      ---
      });
    };

    주로 몇 가지 일 을 했다.
  • 압축 지시 가 있 는 utils.forEach 경우 상태 코드 여부 & 에 따라 압축 할 필요 가 있 는 지 여 부 를 결정 한 다음 에 머리 를 삭제 하여 후속 작업 을 헷 갈 리 지 않도록 한다.
  • HTTP 프로 토 콜 에서 204 No Content 성공 상태 응답 코드 는 현재 요청 이 성 공 했 음 을 표시 하지만 클 라 이언 트 는 기 존 페이지
  • 를 업데이트 할 필요 가 없습니다.
  • 패키지 serializedParams 대상
  • content-encoding 에 따라 응답 데 이 터 를 어떻게 분석 하고 업데이트 할 지 결정 합 니 다 204.
  • stream 은 직접 할당
  • 그렇지 않 으 면 이용 response 사건 분석
  • /*eslint consistent-return:0*/
    module.exports = function httpAdapter(config) {
      return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
        // ---      ---
        
       // Handle errors
        req.on('error', function handleRequestError(err) {
          if (req.aborted) return;
          reject(enhanceError(err, config, null, req));
        });
    
        // Handle request timeout
        if (config.timeout) {
          // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
          // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
          // 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.
          // And then these socket which be hang up will devoring CPU little by little.
          // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
          req.setTimeout(config.timeout, function handleRequestTimeout() {
            req.abort();
            reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
          });
        }
    
        if (config.cancelToken) {
          // Handle cancellation
          config.cancelToken.promise.then(function onCanceled(cancel) {
            if (req.aborted) return;
    
            req.abort();
            reject(cancel);
          });
        }
    
        // Send the request
        if (utils.isStream(data)) {
          data.on('error', function handleStreamError(err) {
            reject(enhanceError(err, config, null, req));
          }).pipe(req);
        } else {
          req.end(data);
        }
      });
    };

    요청 에 실 패 했 습 니 다. 시간 초과, 대응 하 는 작업 처 리 를 취소 합 니 다. data 자체 가 steam 형식 이 라면 직접 감청 합 니 다.
    http adapter 소스 코드 여기까지 입 니 다.
    axios/lib/adapters/xhr.js
    핵심 은 브 라 우 저의 XML HttpRequest 대상 입 니 다.
    대부분의 방법 이 http adapter 에서 말 했 기 때문에 여 기 는 빠르게 지나 갈 수 있 습 니 다.
    var utils = require('./../utils');
    var settle = require('./../core/settle');
    var buildURL = require('./../helpers/buildURL');
    var buildFullPath = require('../core/buildFullPath');
    var parseHeaders = require('./../helpers/parseHeaders');
    var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
    var createError = require('../core/createError');
    
    module.exports = function xhrAdapter(config) {
      return new Promise(function dispatchXhrRequest(resolve, reject) {
        var requestData = config.data;
        var requestHeaders = config.headers;
        //    FormData    Content-Type       
        if (utils.isFormData(requestData)) {
          delete requestHeaders['Content-Type']; // Let the browser set it
        }
    
        //           
        var request = new XMLHttpRequest();
    
        // HTTP basic authentication
        if (config.auth) {
          var username = config.auth.username || '';
          var password = config.auth.password || '';
          requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
        }
    
        var fullPath = buildFullPath(config.baseURL, config.url);
        request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
    
        // Set the request timeout in MS
        request.timeout = config.timeout;
    
        // Listen for ready state
        request.onreadystatechange = function handleLoad() {
          if (!request || request.readyState !== 4) {
            return;
          }
    
          // The request errored out and we didn't get a response, this will be
          // handled by onerror instead
          // With one exception: request that using file: protocol, most browsers
          // will return status as 0 even though it's a successful request
          if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
            return;
          }
    
          // Prepare the response
          var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
          var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
          var response = {
            data: responseData,
            status: request.status,
            statusText: request.statusText,
            headers: responseHeaders,
            config: config,
            request: request
          };
    
          settle(resolve, reject, response);
    
          // Clean up request
          request = null;
        };
    
        // Handle browser request cancellation (as opposed to a manual cancellation)
        request.onabort = function handleAbort() {
          if (!request) {
            return;
          }
    
          reject(createError('Request aborted', config, 'ECONNABORTED', request));
    
          // Clean up request
          request = null;
        };
    
        // Handle low level network errors
        request.onerror = function handleError() {
          // Real errors are hidden from us by the browser
          // onerror should only fire if it's a network error
          reject(createError('Network Error', config, null, request));
    
          // Clean up request
          request = null;
        };
    
        // Handle timeout
        request.ontimeout = function handleTimeout() {
          reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
            request));
    
          // Clean up request
          request = null;
        };
    
        // Add xsrf header
        // This is only done if running in a standard browser environment.
        // Specifically not if we're in a web worker, or react-native.
        if (utils.isStandardBrowserEnv()) {
          var cookies = require('./../helpers/cookies');
    
          // Add xsrf header
          var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
            cookies.read(config.xsrfCookieName) :
            undefined;
    
          if (xsrfValue) {
            requestHeaders[config.xsrfHeaderName] = xsrfValue;
          }
        }
    
        // Add headers to the request
        if ('setRequestHeader' in request) {
          utils.forEach(requestHeaders, function setRequestHeader(val, key) {
            if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
              // Remove Content-Type if data is undefined
              delete requestHeaders[key];
            } else {
              // Otherwise add header to the request
              request.setRequestHeader(key, val);
            }
          });
        }
    
        // Add withCredentials to request if needed
        if (config.withCredentials) {
          request.withCredentials = true;
        }
    
        // Add responseType to request if needed
        if (config.responseType) {
          try {
            request.responseType = config.responseType;
          } catch (e) {
            // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
            // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
            if (config.responseType !== 'json') {
              throw e;
            }
          }
        }
    
        // Handle progress if needed
        if (typeof config.onDownloadProgress === 'function') {
          request.addEventListener('progress', config.onDownloadProgress);
        }
    
        // Not all browsers support upload events
        if (typeof config.onUploadProgress === 'function' && request.upload) {
          request.upload.addEventListener('progress', config.onUploadProgress);
        }
    
        if (config.cancelToken) {
          // Handle cancellation
          config.cancelToken.promise.then(function onCanceled(cancel) {
            if (!request) {
              return;
            }
    
            request.abort();
            reject(cancel);
            // Clean up request
            request = null;
          });
        }
    
        if (requestData === undefined) {
          requestData = null;
        }
    
        // Send the request
        request.send(requestData);
      });
    };

    내용 이 대체로 같 으 니 말 하지 않 겠 다

    좋은 웹페이지 즐겨찾기