« index

Coverage for /Users/yunong/workspace/node-restify/lib/plugins/cors.js : 91%

127 lines | 116 run | 11 missing | 0 partial | 11 blocks | 6 blocks run | 5 blocks missing

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

  // Copyright 2013 Mark Cavage, Inc.  All rights reserved.
  
  var assert = require('assert-plus');
  
  
  ///--- Globals
  
  var ALLOW_HEADERS = [
      'accept',
      'accept-version',
      'content-type',
      'request-id',
      'origin',
      'x-api-version',
      'x-request-id'
  ];
  
  var EXPOSE_HEADERS = [
      'api-version',
      'content-length',
      'content-md5',
      'content-type',
      'date',
      'request-id',
      'response-time'
  ];
  
  // Normal
  var AC_ALLOW_ORIGIN = 'Access-Control-Allow-Origin';
  var AC_ALLOW_CREDS = 'Access-Control-Allow-Credentials';
  var AC_EXPOSE_HEADERS = 'Access-Control-Expose-Headers';
  
  
  ///--- Internal Functions
  
  function matchOrigin(req, origins) {
      var origin = req.headers['origin'];
  
      function belongs(o) {
          if (origin === o || o === '*') {
              origin = o;
              return (true);
          }
  
          return (false);
      }
  
      return ((origin && origins.some(belongs)) ? origin : false);
  }
  
  
  ///--- API
  
  //
  // From http://www.w3.org/TR/cors/#resource-processing-model
  //
  // If "simple" request (paraphrased):
  //
  // 1. If the Origin header is not set, or if the value of Origin is not a
  //    case-sensitive match to any values listed in `opts.origins`, do not
  //    send any CORS headers
  //
  // 2. If the resource supports credentials add a single
  //    'Access-Control-Allow-Credentials' header with the value as "true", and
  //    ensure 'AC-Allow-Origin' is not '*', but is the request header value,
  //    otherwise add a single Access-Control-Allow-Origin header, with either the
  //    value of the Origin header or the string "*" as value
  //
  // 3. Add Access-Control-Expose-Headers as appropriate
  //
  // Pre-flight requests are handled by the router internally
  //
  function cors(opts) {
      assert.optionalObject(opts, 'options');
      opts = opts || {};
      assert.optionalArrayOfString(opts.origins, 'options.origins');
      assert.optionalBool(opts.credentials, 'options.credentials');
      assert.optionalArrayOfString(opts.headers, 'options.headers');
  
      cors.credentials = opts.credentials;
      cors.origins = opts.origins || ['*'];
  
      var headers = (opts.headers || []).slice(0);
      var origins = opts.origins || ['*'];
  
      EXPOSE_HEADERS.forEach(function (h) {
          if (headers.indexOf(h) === -1)
              headers.push(h);
      });
  
      // Handler for simple requests
      function restifyCORSSimple(req, res, next) {
          var origin;
          if (!(origin = matchOrigin(req, origins))) {
              next();
              return;
          }
  
          function corsOnHeader() {
              origin = req.headers['origin'];
              if (opts.credentials) {
                  res.setHeader(AC_ALLOW_ORIGIN, origin);
                  res.setHeader(AC_ALLOW_CREDS, 'true');
              } else {
                  res.setHeader(AC_ALLOW_ORIGIN, origin);
              }
  
              res.setHeader(AC_EXPOSE_HEADERS, headers.join(', '));
          }
  
          res.once('header', corsOnHeader);
          next();
      }
  
      return (restifyCORSSimple);
  }
  
  
  ///--- Exports
  
  module.exports = cors;
  // All of these are needed for the pre-flight code over in lib/router.js
  cors.ALLOW_HEADERS = ALLOW_HEADERS;
  cors.EXPOSE_HEADERS = EXPOSE_HEADERS;
  cors.credentials = false;
  cors.origins = [];
  cors.matchOrigin = matchOrigin;
« index | cover.io