« index

Coverage for /Users/yunong/workspace/node-restify/lib/plugins/static.js : 78%

138 lines | 109 run | 29 missing | 6 partial | 23 blocks | 9 blocks run | 14 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

128

129

130

131

132

133

134

135

136

137

138

  // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
  
  var fs = require('fs');
  var path = require('path');
  var escapeRE = require('escape-regexp-component');
  
  var assert = require('assert-plus');
  var mime = require('mime');
  var errors = require('../errors');
  
  
  ///--- Globals
  
  var MethodNotAllowedError = errors.MethodNotAllowedError;
  var NotAuthorizedError = errors.NotAuthorizedError;
  var ResourceNotFoundError = errors.ResourceNotFoundError;
  
  
  ///--- Functions
  
  function serveStatic(opts) {
      opts = opts || {};
      assert.object(opts, 'options');
      assert.string(opts.directory, 'options.directory');
      assert.optionalNumber(opts.maxAge, 'options.maxAge');
      assert.optionalObject(opts.match, 'options.match');
      assert.optionalString(opts.charSet, 'options.charSet');
  
      var p = path.normalize(opts.directory).replace(/\\/g, '/');
      var re = new RegExp('^' + escapeRE(p) + '/?.*');
  
      function serveFileFromStats(file, err, stats, isGzip, req, res, next) {
          if (err) {
              next(new ResourceNotFoundError(err,
                  req.path()));
              return;
          } else if (!stats.isFile()) {
              next(new ResourceNotFoundError('%s does not exist', req.path()));
              return;
          }
  
          if (res.handledGzip && isGzip) {
              res.handledGzip();
          }
  
          var fstream = fs.createReadStream(file + (isGzip ? '.gz' : ''));
          var maxAge = opts.maxAge === undefined ? 3600 : opts.maxAge;
          fstream.once('open', function (fd) {
              res.cache({maxAge: maxAge});
              res.set('Content-Length', stats.size);
              res.set('Content-Type', mime.lookup(file));
              res.set('Last-Modified', stats.mtime);
              if (opts.charSet) {
                  var type = res.getHeader('Content-Type') +
                      '; charset=' + opts.charSet;
                  res.setHeader('Content-Type', type);
              }
              if (opts.etag) {
                  res.set('ETag', opts.etag(stats, opts));
              }
              res.writeHead(200);
              fstream.pipe(res);
              fstream.once('end', function () {
                  next(false);
              });
          });
      }
  
      function serveNormal(file, req, res, next) {
          fs.stat(file, function (err, stats) {
              if (!err && stats.isDirectory() && opts.default) {
                  // Serve an index.html page or similar
                  file = path.join(file, opts.default);
                  fs.stat(file, function (dirErr, dirStats) {
                      serveFileFromStats(file,
                          dirErr,
                          dirStats,
                          false,
                          req,
                          res,
                          next);
                  });
              } else {
                  serveFileFromStats(file,
                      err,
                      stats,
                      false,
                      req,
                      res,
                      next);
              }
          });
      }
  
      function serve(req, res, next) {
          var file = path.join(opts.directory,
              decodeURIComponent(req.path()));
  
          if (req.method !== 'GET' && req.method !== 'HEAD') {
              next(new MethodNotAllowedError(req.method));
              return;
          }
  
          if (!re.test(file.replace(/\\/g, '/'))) {
              next(new NotAuthorizedError(req.path()));
              return;
          }
  
          if (opts.match && !opts.match.test(file))opts.match.test(file)) {
              next(new NotAuthorizedError(req.path()));
              return;
          }
  
          if (opts.gzip && req.acceptsEncoding('gzip')) {
              fs.stat(file + '.gz', function (err, stats) {
                  if (!err) {
                      res.setHeader('Content-Encoding', 'gzip');
                      serveFileFromStats(file,
                          err,
                          stats,
                          true,
                          req,
                          res,
                          next);
                  } else {
                      serveNormal(file, req, res, next);
                  }
              });
          } else {
              serveNormal(file, req, res, next);
          }
  
      }
  
      return (serve);
  }
  
  module.exports = serveStatic;
« index | cover.io