• 解析node-cors模块


    (function () {
    
      'use strict';
    
      var assign = require('object-assign');
      var vary = require('vary');
    
      var defaults = {
          origin: '*',
          methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
          preflightContinue: false,
          optionsSuccessStatus: 204
        };
    
      function isString(s) {
        return typeof s === 'string' || s instanceof String;
      }
    
      function isOriginAllowed(origin, allowedOrigin) {
        // 多次递归返回一个地址
        if (Array.isArray(allowedOrigin)) {
          for (var i = 0; i < allowedOrigin.length; ++i) {
            if (isOriginAllowed(origin, allowedOrigin[i])) {
              return true;
            }
          }
          return false;
        // 在白名单里面就通过  
        } else if (isString(allowedOrigin)) {
          return origin === allowedOrigin;
        // 正则匹配  
        } else if (allowedOrigin instanceof RegExp) {
          return allowedOrigin.test(origin);
        } else {
          return !!allowedOrigin;
        }
      }
    
      function configureOrigin(options, req) {
        var requestOrigin = req.headers.origin,
          headers = [],
          isAllowed;
        // 默认所有域名都可以使用
        if (!options.origin || options.origin === '*') {
          // allow any origin
          headers.push([{
            key: 'Access-Control-Allow-Origin',
            value: '*'
          }]);
        // 固定的一个域名
        } else if (isString(options.origin)) {
          // fixed origin
          headers.push([{
            key: 'Access-Control-Allow-Origin',
            value: options.origin
          }]);
          headers.push([{
            key: 'Vary',
            value: 'Origin'
          }]);
        } else {
          // 这里通过白名单列表 
          isAllowed = isOriginAllowed(requestOrigin, options.origin);
          // reflect origin
          headers.push([{
            key: 'Access-Control-Allow-Origin',
            value: isAllowed ? requestOrigin : false
          }]);
          headers.push([{
            key: 'Vary',
            value: 'Origin'
          }]);
        }
    
        return headers;
      }
    
      // 允许的方法
      function configureMethods(options) {
        var methods = options.methods;
        if (methods.join) {
          methods = options.methods.join(','); // .methods is an array, so turn it into a string
        }
        return {
          key: 'Access-Control-Allow-Methods',
          value: methods
        };
      }
    
      function configureCredentials(options) {
        // 是否带cookies
        if (options.credentials === true) {
          return {
            key: 'Access-Control-Allow-Credentials',
            value: 'true'
          };
        }
        return null;
      }
    
      function configureAllowedHeaders(options, req) {
        var allowedHeaders = options.allowedHeaders || options.headers;
        var headers = [];
    
        // 如果没有请求头, 那么指定请求头
        if (!allowedHeaders) {
          allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers
          headers.push([{
            key: 'Vary',
            value: 'Access-Control-Request-Headers'
          }]);
        } else if (allowedHeaders.join) {
          allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string
        }
        if (allowedHeaders && allowedHeaders.length) {
          headers.push([{
            key: 'Access-Control-Allow-Headers',
            value: allowedHeaders
          }]);
        }
    
        return headers;
      }
    
      // 该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
      // 如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
      // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
      // https://github.com/expressjs/cors#configuration-options
      function configureExposedHeaders(options) {
        var headers = options.exposedHeaders;
        if (!headers) {
          return null;
        } else if (headers.join) {
          headers = headers.join(','); // .headers is an array, so turn it into a string
        }
        if (headers && headers.length) {
          return {
            key: 'Access-Control-Expose-Headers',
            value: headers
          };
        }
        return null;
      }
    
      // 在此时间内不需要发出预检查请求
      function configureMaxAge(options) {
        var maxAge = options.maxAge && options.maxAge.toString();
        if (maxAge && maxAge.length) {
          return {
            key: 'Access-Control-Max-Age',
            value: maxAge
          };
        }
        return null;
      }
    
      // 发送响应头
      function applyHeaders(headers, res) {
        for (var i = 0, n = headers.length; i < n; i++) {
          var header = headers[i];
          if (header) {
            if (Array.isArray(header)) {
              applyHeaders(header, res);
            } else if (header.key === 'Vary' && header.value) {
              vary(res, header.value);
            } else if (header.value) {
              res.setHeader(header.key, header.value);
            }
          }
        }
      }
    
      function cors(options, req, res, next) {
        var headers = [],
          method = req.method && req.method.toUpperCase && req.method.toUpperCase();
        // 嗅探请求 如果是非简单请求,那么会先发送这个
        // 关于简单请求和非简单请求
        // 这里写的很详细 http://www.lcode.cc/2016/12/06/cors-explain.html
        if (method === 'OPTIONS') {
          // preflight
          headers.push(configureOrigin(options, req));
          headers.push(configureCredentials(options, req));
          headers.push(configureMethods(options, req));
          headers.push(configureAllowedHeaders(options, req));
          headers.push(configureMaxAge(options, req));
          headers.push(configureExposedHeaders(options, req));
          applyHeaders(headers, res);
    
          // 兼容一波zz浏览器
          if (options.preflightContinue ) {
            next();
          } else {
            // Safari (and potentially other browsers) need content-length 0,
            //   for 204 or they just hang waiting for a body
            res.statusCode = options.optionsSuccessStatus || defaults.optionsSuccessStatus;
            res.setHeader('Content-Length', '0');
            res.end();
          }
        } else {
          // 返回正常的结果
          // actual response
          headers.push(configureOrigin(options, req));
          headers.push(configureCredentials(options, req));
          headers.push(configureExposedHeaders(options, req));
          applyHeaders(headers, res);
          next();
        }
      }
    
      function middlewareWrapper(o) {
        // 设置回调函数
        // if options are static (either via defaults or custom options passed in), wrap in a function
        var optionsCallback = null;
        if (typeof o === 'function') {
          optionsCallback = o;
        } else {
          optionsCallback = function (req, cb) {
            cb(null, o);
          };
        }
        // 使用到了闭包
        return function corsMiddleware(req, res, next) {
          optionsCallback(req, function (err, options) {
            if (err) {
              // 出错了直接进入下一个中间件
              next(err);
            } else {
              var corsOptions = assign({}, defaults, options);
              var originCallback = null;
              // 通过传参数进来可以动态使用cors。
              // 见文档 https://github.com/expressjs/cors#configuring-cors-w-dynamic-origin
              if (corsOptions.origin && typeof corsOptions.origin === 'function') {
                originCallback = corsOptions.origin;
              } else if (corsOptions.origin) {
                originCallback = function (origin, cb) {
                  cb(null, corsOptions.origin);
                };
              }
    
              if (originCallback) {
                // 用来设置白名单
                originCallback(req.headers.origin, function (err2, origin) {
                  if (err2 || !origin) {
                    next(err2);
                  } else {
                    corsOptions.origin = origin;
                    cors(corsOptions, req, res, next);
                  }
                });
              } else {
                next();
              }
            }
          });
        };
      }
    
      // can pass either an options hash, an options delegate, or nothing
      module.exports = middlewareWrapper;
    
    }());
    

      

  • 相关阅读:
    Shell脚本编程-02-----shell编程之条件语句
    ELK 简介
    Linux 下的网卡文件配置
    Tomcat 简介
    Docker 基本操作
    zabbix 介绍
    CentOS 上搭建 Kubernetes 集群
    Docker 简介
    yum 源的配置安装
    Docker 入门
  • 原文地址:https://www.cnblogs.com/web-alibaba/p/7583326.html
Copyright © 2020-2023  润新知