From 6a778e8c17adf37cc76b6f70f0388b174475a7ed Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Wed, 12 Nov 2014 14:14:37 +0300 Subject: [PATCH] change code style to jshttp close #155, see reasons there This is a huge commit, so let me know if it will cause any trouble, I might consider reverting it if it's the case. --- .editorconfig | 11 +- Gruntfile.js | 66 +-- lib/GUI/css/responsive.less | 58 +- lib/GUI/css/styles.less | 288 ++++----- lib/GUI/entry.hbs | 36 +- lib/GUI/index.hbs | 222 +++---- lib/GUI/js/entry.js | 118 ++-- lib/GUI/js/main.js | 5 +- lib/GUI/js/search.js | 156 +++-- lib/auth.js | 391 ++++++------- lib/cli.js | 208 +++---- lib/config.js | 308 +++++----- lib/config_gen.js | 24 +- lib/index-web.js | 217 +++---- lib/index.js | 831 +++++++++++++------------- lib/local-fs.js | 408 ++++++------- lib/local-list.js | 62 +- lib/local-storage.js | 1001 ++++++++++++++++---------------- lib/logger.js | 247 ++++---- lib/middleware.js | 255 ++++---- lib/search.js | 89 +-- lib/status-cats.js | 122 ++-- lib/storage.js | 845 ++++++++++++++------------- lib/streams.js | 63 +- lib/up-storage.js | 625 ++++++++++---------- lib/utils.js | 228 ++++---- test/functional/addtag.js | 102 ++-- test/functional/adduser.js | 54 +- test/functional/basic.js | 246 ++++---- test/functional/gh29.js | 115 ++-- test/functional/gzip.js | 152 ++--- test/functional/incomplete.js | 113 ++-- test/functional/index.js | 127 ++-- test/functional/lib/package.js | 16 +- test/functional/lib/server.js | 203 +++---- test/functional/lib/startup.js | 63 +- test/functional/mirror.js | 113 ++-- test/functional/newnpmreg.js | 220 +++---- test/functional/nullstorage.js | 118 ++-- test/functional/race.js | 156 ++--- test/functional/racycrash.js | 111 ++-- test/functional/security.js | 143 +++-- test/functional/tags.js | 79 ++- test/unit/config_def.js | 8 +- test/unit/mystreams.js | 24 +- test/unit/no_proxy.js | 144 ++--- test/unit/parse_interval.js | 58 +- test/unit/st_merge.js | 88 +-- test/unit/tag_version.js | 92 +-- test/unit/utils.js | 70 +-- test/unit/validate_all.js | 52 +- 51 files changed, 4767 insertions(+), 4784 deletions(-) diff --git a/.editorconfig b/.editorconfig index d99d62129b0a..c65e480e9815 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,16 +6,7 @@ root = true end_of_line = lf insert_final_newline = true -# Tab indentation (no size specified) -[*.js] -indent_style = tab - # 2 space indentation -[{.,}*.y{a,}ml] +[{.,}*.{js,yml,yaml}] indent_style = space indent_size = 2 - -# Indentation override for Gruntfile -[Gruntfile.js] -indent_style = space -indent_size = 4 diff --git a/Gruntfile.js b/Gruntfile.js index 83e51e911c8a..f14e61230f34 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,39 +1,39 @@ module.exports = function(grunt) { - grunt.initConfig({ - pkg: grunt.file.readYAML('package.yaml'), - browserify: { - dist: { - files: { - 'lib/static/main.js': ['lib/GUI/js/main.js'] - }, - options: { - debug: true, - transform: ['browserify-handlebars'] - } - } + grunt.initConfig({ + pkg: grunt.file.readYAML('package.yaml'), + browserify: { + dist: { + files: { + 'lib/static/main.js': [ 'lib/GUI/js/main.js' ] }, - less: { - dist: { - files: { - 'lib/static/main.css': ['lib/GUI/css/main.less'] - }, - options: { - sourceMap: true - } - } + options: { + debug: true, + transform: [ 'browserify-handlebars' ] + } + } + }, + less: { + dist: { + files: { + 'lib/static/main.css': [ 'lib/GUI/css/main.less' ] }, - watch: { - files: [ "lib/GUI/**/*"], - tasks: [ 'default' ] + options: { + sourceMap: true } - }); + } + }, + watch: { + files: [ 'lib/GUI/**/*' ], + tasks: [ 'default' ] + } + }) - grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-browserify') + grunt.loadNpmTasks('grunt-contrib-watch') + grunt.loadNpmTasks('grunt-contrib-less') - grunt.registerTask('default', [ - 'browserify', - 'less' - ]); -}; + grunt.registerTask('default', [ + 'browserify', + 'less' + ]) +} diff --git a/lib/GUI/css/responsive.less b/lib/GUI/css/responsive.less index 63f00af777ed..4e6b413c6c9f 100644 --- a/lib/GUI/css/responsive.less +++ b/lib/GUI/css/responsive.less @@ -1,38 +1,38 @@ @media (max-width: 992px) { - .body { - .main-header { - .npm-logo { - width: 100px; - float: left; - } + .body { + .main-header { + .npm-logo { + width: 100px; + float: left; + } - .packages-header { - border-bottom: none; - } - } - } + .packages-header { + border-bottom: none; + } + } + } } @media (max-width: 768px) { - .body { - .content { - padding-top: @mainHeaderHeight + @packagesHeaderHeight + @smRegistryInfoHeight + 10; + .body { + .content { + padding-top: @mainHeaderHeight + @packagesHeaderHeight + @smRegistryInfoHeight + 10; - .entry { - .title { - margin-bottom: 0; - } + .entry { + .title { + margin-bottom: 0; + } - .author { - float: none !important; - clear: both; - padding: 0 0 5px 18px; - } - } - } + .author { + float: none !important; + clear: both; + padding: 0 0 5px 18px; + } + } + } - .no-results { - margin: 10px 0 0; - } - } + .no-results { + margin: 10px 0 0; + } + } } diff --git a/lib/GUI/css/styles.less b/lib/GUI/css/styles.less index e1f31f271097..1742b1f72906 100644 --- a/lib/GUI/css/styles.less +++ b/lib/GUI/css/styles.less @@ -9,194 +9,194 @@ /*** Main Styles ***/ .body { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left:0; - margin: 0; - padding: 0; - - .main-header { - position: fixed; - top: 0; - left: 0; - right: 0; - background: @white; - z-index: 1; - - .navbar { - margin-bottom: 0; - } - - .npm-logo { - width: 79px; - height: @mainHeaderHeight; - background-image: url( -/logo-sm ); - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAoCAYAAAC/xadkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMDA4QUE4NzQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMDA4QUE4ODQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMwMDhBQTg1NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMwMDhBQTg2NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ypkYZgAACelJREFUeNrsWmtsHFcZPXd2Zh9+r+31+5W4tSPSoKZUKgpIgdAWqorSULVFqJREKpVAAqRKhfIPKfCDtP2TKilpRRACUpBC+UlbQRG0RUmo0gBtWoyT2HHstR0/Yq/X9j5mh/PdmfX7te42tZyMdO2dmTt3vnvu+Z53lHOm9VcwQntRuvMq0qMKmTSgDNw4Fh4OG3Gxyh3E/l2GZOxVE0UtDShqLUbCLkb9o0D6KmAnbgA4D7cMcbMIXAUw+BoQ2QXEu5pMAjqB2q8Cl34BXDwC1D3IzkkgNUEAfTeAE+B8IWJhAl0vAKECoOa7wIVDEwbsaQUMA03fAMJbgJ5fAxlS1Crjg/Z1DhznbxbzB1l3+SWguBpo2cdzamd6wjBcdqXYeoHa+8m8T/PncSBJ5gUiHCB9nQLHefvLCRK1sPe3QNUtQAO1ElE2MWumcJHEg881iE43ULGbQIdJ0RNA5V1EuxWY7r++VFiAC5JlcRLqyp+olV8CSm938RGn4foDwyR2xowzcQy3Q8l24KYi6vVviHyc6ryTAPa5OLt/Nq9HddiCddTM94GxvwFbHwIKb6YpE+CEa9n5K2HewoOXMj1AAQfY9h2g8xi1mjpevYdATnoob+JQxEeHcOUNauYHQNtjVN0KFw8sgEppFyJQLmQTDWRmgP3LXQDPU+ejpK+/lNeTm5R9jlZEpEgQRbJse5z//ZxvdDFw7mHM5eHCe2wxtnqglfHfMEOZcJt7eVOyjzA4hGP0TaD8EZ7TWeKC5w+WfsBc1o7xVvpqCom+Dhi+KWCSIMbb+QLxzJnNCZ6iyo50w4l2kHRjCDYTHstw7eDi/ssxj51VGMOvnsK5b55Awc0RbwXObn6HQVW1Y2/DoMbu/PN+BBoreTm21LxJL0MtO5AiroKvMg0XY8fa/GGKIZ7AdCMzzatltEwJMkl4DsNZDJ7PoPOxYIQs/vZhuqsL9tRkbuwj5YNNTfCVlOhUJzM1hamLXbnPyW8huGWr+9uykIj2ITU8nD9NoJxmaSlVtYmWyeY71GpDk3mJtLGMN5kTM6ZJ5Rgan3gCwYZGYrB2m6eYxPQfP474++f072BjI5p/8EPK6qzZ78hzqZER9L34ol7U6YEBRO67D6W7djGDyo/9lXfE33sX/S8d50IVr7IoyrN5RSZ/rZLD2jbsiQlUP/wwAnX1OQs2+vpfMPbWmxTKD5MMrHrwodyJkUrj8pHDWuzk4ABKP/NZVD3wQF41dowL2/PcIQRqi9eGNxy1Ou/F9lFtk/396xIqk0jQa1nalgiL13Mk+nq9kJQpkeVnJHA17+ZOzIDi2Gu0BBKq5KdwlxocRCaVdGPNUAhWRcU6ihhpjnNF2x+HbDc5hq+gIOdx7PExpMfHKYihF91fVT0TVNjxONI0AbKQ8h4rEtELm7tXltGh8mJxz+3fh7fvuAOnb7sVnU8+uT52RaN454t348wX9uDUJ3dg+JVX1jVOz+HDOLnjFpzZvRv/2bsX9uTkzL0rf3wZJ7d/Au/cfRfO7Pk8ndfFnB2LDttsnZ7lx12Jatr0pPI/k0yuz2hnHGSmp8nglDthe30qLqYhMzkFm2NJmxvkZsT5UU5p2oQ4OWZLKpvKubltXmpNvqIi7eoFPF9haJ0xlqEdioCXIXi5q5M3TCCgZZGxfMXF85RLO62yMn1PwBMvmzt6oraGtnf5CZQcZ07L0zj4iMbJ0zuMDZdtCROyplht5FRQ6eh4w5SIxcMm6TREbRP0umK3Nmbdb149b4OsJW1cQVubdjhmRaW2WxuYefnztvk4AnV12Pn6X+es8Mau4Bj5CpI/kvragixnxfNVMqS8jLPYOq++vI4X8ftr61bIVdzUy6G9MoLzQxUjGOT15IciUqC+fsZDarWWKs1yYZOEIZRDZJay2twsxSoLu3GoNydhe/bwM9tw1hSjuhMx4RXWV07iTJjFJeh9/oiehBQJsgNI+qMsE+mxMZjhsBZoqqsLvUd/rstPkqolenrgr65m2hRb8T2Sr/a+cJTBcQYq4PdsswODk0+PjupSlDAl2FCvs4/U8NA8Wdx4sxDjb/2DC12r3y0LeumZpxn7BaH8FibOnkWQgIl9NcidS88+C7OyQsd7kx0d8NdUr4GNTtbmqZULV1IUMH0wykrR98tjFDYOwzTnsVLqdKGtrTr4FCESPd3ofOop9vNpMIPNzbAqIwR4fOX8mAB1HTigmSHsUToY5d+0rQPfUOtWLbevphZjb/wdg384MU+WbHYhwAkI8pycdx88qOWQUpq/shKBhgaXYYbCZRIi42UhIn9wSzMXZWIVAGeZt+wGUCbBRH1oElZVmGy4CrOonG3phN+OJXTLGvpATZM3G7LICripltQBVzCxFoUvaG/XoYrJxbLH4pxYcuaZ1FDMW3epcgdm37EoV3QY8swWSq2K2nkBciI6JJGGC0BpBCidZVQyOoJ03A+fOe3VLdVypFKzm96LeQCjKIRASx38RYNAMAJd2HcyOdt8J5FhTikfzJCJUk2WVRcm2PbMhBSZJQVPPb7YowQBLAkC4dA13KwTKNLwDfXAKG+nxgX1+fIlKdub4jwBZVIZVH55B8q3n2KHTwF1j7J7LDfwZD/AF8EHj/+MNuo0gi31SA0M6IqGZuHc9Mhwd6mUPwAf29T5KNqf/x4iX7mTxj96bcIWzXA6m77fQzlnYNSUecDEMb/a7ngZhiPfWBhz0CPSRg3/jUJ1H4WvSr5X2SfFHLaCHCchfUvd2hnBEhsorEsP9Hs2Zc5Y+hs4n1ZXfZ2q5yukkTfl+YlrFPM5btv6feL1O+B/zwHNj9DV1y7e/NYZhiXbZxmvWCDANQOxd4Gel4EIQSu+nzc6PPoa61CDKUxdGMDEQI/+YkvbkRWMcbJ/Qv+Pj/QgOTCqnweuFXhZAOnYCr8GlNO+d9L7N95DQt7GW5e8OSlPbXUZ3puQ2gIMvwZET/OBH5M0O9jnPS/9XW8KPIb6b9+J8J52WOFi1zuvpTI9EkPJ7S3Q38LhWsbx2Z1Ezrvyc7T1jUD3AaCK2hK5l7f6kN2OVE7nPf9F67fa9Nnl41xk2rUtBC4gmyBdEjp+SGGEsYydUAj3O8C1MkhqeYMuC/Bx7RdLwEzwJMC/8BMgRNmbvi4hO6E51mXCX8Yr9CqdT7sdt/3UEzgfwGWjoSGvrYcFH+dGu8z/snzEDbQ/QwAP0g4eAm76ERUxrJRz/t4hFLVUwLqVYcF+dpbv0CaxeC93qUTdWUD1jVQ2Ujn0XS2/TrtsA4P0UWpn4qR8rzhuYvRfHUhNtaG8OoZLjyk3FDFmH1ZzQpn5HwWpZZP42WtqWaHUjOxqaQeiVkv21Rr7L86gFoumlh5j7nnGRSLY5iDeX4jRf3b9X4ABADgctRMdEJWtAAAAAElFTkSuQmCC); - background-repeat: no-repeat; - background-position: center center; - - >a { - display: block; - width: 100%; - height: 100%; - } - } - - .setup { - line-height: 1.3em; - padding-top: 5px; - } - - .packages-header { - border-bottom: @headerBorderWidth solid #e6e6e6; - - .search-container { - top: 9px; - - .search-icon { - background: #e6e6e6; - } - } - } - - .sm-registry-info { - height: @smRegistryInfoHeight; - line-height: 1.7em; - } - } - - .content { - padding-top: @mainHeaderHeight + @packagesHeaderHeight + 10; - - .entry { - .transition(height .3s); - padding: 9px 10px; - overflow: hidden; - border-bottom: 1px solid #E7E7E7; - - &:last-child { - border-bottom: none; - } - - &:nth-child( even ) { - background: @entryBg; - } - - .title { - margin: 0 0 5px 10px; - } - - .description { - margin: 0 0 0 18px; - font-size: 13px; - } - - .name:hover { - text-decoration: none; - } - - .name:before { - margin: 0; - margin-left: -10px; - .transformTransition(.2s); - } - - &.open .name:before { - .rotate(90deg); - } - - .version { - color: #666; - } - - .author { - color: #666; - } - - .readme { - font-size: 14px; - margin-top: 10px; - background: @white; - padding: 10px 12px; - .border-radius(3px); - border: 1px solid darken( @entryBg, 10% ); - } - } - } + position: absolute; + top: 0; + right: 0; + bottom: 0; + left:0; + margin: 0; + padding: 0; + + .main-header { + position: fixed; + top: 0; + left: 0; + right: 0; + background: @white; + z-index: 1; + + .navbar { + margin-bottom: 0; + } + + .npm-logo { + width: 79px; + height: @mainHeaderHeight; + background-image: url( -/logo-sm ); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAoCAYAAAC/xadkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMDA4QUE4NzQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMDA4QUE4ODQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMwMDhBQTg1NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMwMDhBQTg2NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ypkYZgAACelJREFUeNrsWmtsHFcZPXd2Zh9+r+31+5W4tSPSoKZUKgpIgdAWqorSULVFqJREKpVAAqRKhfIPKfCDtP2TKilpRRACUpBC+UlbQRG0RUmo0gBtWoyT2HHstR0/Yq/X9j5mh/PdmfX7te42tZyMdO2dmTt3vnvu+Z53lHOm9VcwQntRuvMq0qMKmTSgDNw4Fh4OG3Gxyh3E/l2GZOxVE0UtDShqLUbCLkb9o0D6KmAnbgA4D7cMcbMIXAUw+BoQ2QXEu5pMAjqB2q8Cl34BXDwC1D3IzkkgNUEAfTeAE+B8IWJhAl0vAKECoOa7wIVDEwbsaQUMA03fAMJbgJ5fAxlS1Crjg/Z1DhznbxbzB1l3+SWguBpo2cdzamd6wjBcdqXYeoHa+8m8T/PncSBJ5gUiHCB9nQLHefvLCRK1sPe3QNUtQAO1ElE2MWumcJHEg881iE43ULGbQIdJ0RNA5V1EuxWY7r++VFiAC5JlcRLqyp+olV8CSm938RGn4foDwyR2xowzcQy3Q8l24KYi6vVviHyc6ryTAPa5OLt/Nq9HddiCddTM94GxvwFbHwIKb6YpE+CEa9n5K2HewoOXMj1AAQfY9h2g8xi1mjpevYdATnoob+JQxEeHcOUNauYHQNtjVN0KFw8sgEppFyJQLmQTDWRmgP3LXQDPU+ejpK+/lNeTm5R9jlZEpEgQRbJse5z//ZxvdDFw7mHM5eHCe2wxtnqglfHfMEOZcJt7eVOyjzA4hGP0TaD8EZ7TWeKC5w+WfsBc1o7xVvpqCom+Dhi+KWCSIMbb+QLxzJnNCZ6iyo50w4l2kHRjCDYTHstw7eDi/ssxj51VGMOvnsK5b55Awc0RbwXObn6HQVW1Y2/DoMbu/PN+BBoreTm21LxJL0MtO5AiroKvMg0XY8fa/GGKIZ7AdCMzzatltEwJMkl4DsNZDJ7PoPOxYIQs/vZhuqsL9tRkbuwj5YNNTfCVlOhUJzM1hamLXbnPyW8huGWr+9uykIj2ITU8nD9NoJxmaSlVtYmWyeY71GpDk3mJtLGMN5kTM6ZJ5Rgan3gCwYZGYrB2m6eYxPQfP474++f072BjI5p/8EPK6qzZ78hzqZER9L34ol7U6YEBRO67D6W7djGDyo/9lXfE33sX/S8d50IVr7IoyrN5RSZ/rZLD2jbsiQlUP/wwAnX1OQs2+vpfMPbWmxTKD5MMrHrwodyJkUrj8pHDWuzk4ABKP/NZVD3wQF41dowL2/PcIQRqi9eGNxy1Ou/F9lFtk/396xIqk0jQa1nalgiL13Mk+nq9kJQpkeVnJHA17+ZOzIDi2Gu0BBKq5KdwlxocRCaVdGPNUAhWRcU6ihhpjnNF2x+HbDc5hq+gIOdx7PExpMfHKYihF91fVT0TVNjxONI0AbKQ8h4rEtELm7tXltGh8mJxz+3fh7fvuAOnb7sVnU8+uT52RaN454t348wX9uDUJ3dg+JVX1jVOz+HDOLnjFpzZvRv/2bsX9uTkzL0rf3wZJ7d/Au/cfRfO7Pk8ndfFnB2LDttsnZ7lx12Jatr0pPI/k0yuz2hnHGSmp8nglDthe30qLqYhMzkFm2NJmxvkZsT5UU5p2oQ4OWZLKpvKubltXmpNvqIi7eoFPF9haJ0xlqEdioCXIXi5q5M3TCCgZZGxfMXF85RLO62yMn1PwBMvmzt6oraGtnf5CZQcZ07L0zj4iMbJ0zuMDZdtCROyplht5FRQ6eh4w5SIxcMm6TREbRP0umK3Nmbdb149b4OsJW1cQVubdjhmRaW2WxuYefnztvk4AnV12Pn6X+es8Mau4Bj5CpI/kvragixnxfNVMqS8jLPYOq++vI4X8ftr61bIVdzUy6G9MoLzQxUjGOT15IciUqC+fsZDarWWKs1yYZOEIZRDZJay2twsxSoLu3GoNydhe/bwM9tw1hSjuhMx4RXWV07iTJjFJeh9/oiehBQJsgNI+qMsE+mxMZjhsBZoqqsLvUd/rstPkqolenrgr65m2hRb8T2Sr/a+cJTBcQYq4PdsswODk0+PjupSlDAl2FCvs4/U8NA8Wdx4sxDjb/2DC12r3y0LeumZpxn7BaH8FibOnkWQgIl9NcidS88+C7OyQsd7kx0d8NdUr4GNTtbmqZULV1IUMH0wykrR98tjFDYOwzTnsVLqdKGtrTr4FCESPd3ofOop9vNpMIPNzbAqIwR4fOX8mAB1HTigmSHsUToY5d+0rQPfUOtWLbevphZjb/wdg384MU+WbHYhwAkI8pycdx88qOWQUpq/shKBhgaXYYbCZRIi42UhIn9wSzMXZWIVAGeZt+wGUCbBRH1oElZVmGy4CrOonG3phN+OJXTLGvpATZM3G7LICripltQBVzCxFoUvaG/XoYrJxbLH4pxYcuaZ1FDMW3epcgdm37EoV3QY8swWSq2K2nkBciI6JJGGC0BpBCidZVQyOoJ03A+fOe3VLdVypFKzm96LeQCjKIRASx38RYNAMAJd2HcyOdt8J5FhTikfzJCJUk2WVRcm2PbMhBSZJQVPPb7YowQBLAkC4dA13KwTKNLwDfXAKG+nxgX1+fIlKdub4jwBZVIZVH55B8q3n2KHTwF1j7J7LDfwZD/AF8EHj/+MNuo0gi31SA0M6IqGZuHc9Mhwd6mUPwAf29T5KNqf/x4iX7mTxj96bcIWzXA6m77fQzlnYNSUecDEMb/a7ngZhiPfWBhz0CPSRg3/jUJ1H4WvSr5X2SfFHLaCHCchfUvd2hnBEhsorEsP9Hs2Zc5Y+hs4n1ZXfZ2q5yukkTfl+YlrFPM5btv6feL1O+B/zwHNj9DV1y7e/NYZhiXbZxmvWCDANQOxd4Gel4EIQSu+nzc6PPoa61CDKUxdGMDEQI/+YkvbkRWMcbJ/Qv+Pj/QgOTCqnweuFXhZAOnYCr8GlNO+d9L7N95DQt7GW5e8OSlPbXUZ3puQ2gIMvwZET/OBH5M0O9jnPS/9XW8KPIb6b9+J8J52WOFi1zuvpTI9EkPJ7S3Q38LhWsbx2Z1Ezrvyc7T1jUD3AaCK2hK5l7f6kN2OVE7nPf9F67fa9Nnl41xk2rUtBC4gmyBdEjp+SGGEsYydUAj3O8C1MkhqeYMuC/Bx7RdLwEzwJMC/8BMgRNmbvi4hO6E51mXCX8Yr9CqdT7sdt/3UEzgfwGWjoSGvrYcFH+dGu8z/snzEDbQ/QwAP0g4eAm76ERUxrJRz/t4hFLVUwLqVYcF+dpbv0CaxeC93qUTdWUD1jVQ2Ujn0XS2/TrtsA4P0UWpn4qR8rzhuYvRfHUhNtaG8OoZLjyk3FDFmH1ZzQpn5HwWpZZP42WtqWaHUjOxqaQeiVkv21Rr7L86gFoumlh5j7nnGRSLY5iDeX4jRf3b9X4ABADgctRMdEJWtAAAAAElFTkSuQmCC); + background-repeat: no-repeat; + background-position: center center; + + >a { + display: block; + width: 100%; + height: 100%; + } + } + + .setup { + line-height: 1.3em; + padding-top: 5px; + } + + .packages-header { + border-bottom: @headerBorderWidth solid #e6e6e6; + + .search-container { + top: 9px; + + .search-icon { + background: #e6e6e6; + } + } + } + + .sm-registry-info { + height: @smRegistryInfoHeight; + line-height: 1.7em; + } + } + + .content { + padding-top: @mainHeaderHeight + @packagesHeaderHeight + 10; + + .entry { + .transition(height .3s); + padding: 9px 10px; + overflow: hidden; + border-bottom: 1px solid #E7E7E7; + + &:last-child { + border-bottom: none; + } + + &:nth-child( even ) { + background: @entryBg; + } + + .title { + margin: 0 0 5px 10px; + } + + .description { + margin: 0 0 0 18px; + font-size: 13px; + } + + .name:hover { + text-decoration: none; + } + + .name:before { + margin: 0; + margin-left: -10px; + .transformTransition(.2s); + } + + &.open .name:before { + .rotate(90deg); + } + + .version { + color: #666; + } + + .author { + color: #666; + } + + .readme { + font-size: 14px; + margin-top: 10px; + background: @white; + padding: 10px 12px; + .border-radius(3px); + border: 1px solid darken( @entryBg, 10% ); + } + } + } } .pkg-search-container { - display: none; + display: none; } .packages-container { - .search-ajax { - display: block; - margin: 50px auto; - } + .search-ajax { + display: block; + margin: 50px auto; + } } .no-results { - text-align: center; - margin: 50px 0; - color: #888; + text-align: center; + margin: 50px 0; + color: #888; - big { - font-size: 38px; - margin-bottom: 8px; - } + big { + font-size: 38px; + margin-bottom: 8px; + } - code { - font-size: 1.2em; - } + code { + font-size: 1.2em; + } } .red { - color: @npmRed; + color: @npmRed; } .light-red { - color: lighten( @npmRed, 10% ); + color: lighten( @npmRed, 10% ); } .white { - color: @white !important; + color: @white !important; } .red-bg { - background: @npmRed; + background: @npmRed; } .light-red-bg { - background: lighten( @npmRed, 10% ); + background: lighten( @npmRed, 10% ); } .no-bg { - background: none !important; + background: none !important; } .no-border { - border: none !important; + border: none !important; } .no-rnd-cnr { - .border-radius( 0 ); + .border-radius( 0 ); } .center { - text-align: center; + text-align: center; } .login-btn { - margin-left: 10px; - margin-top: 5px; + margin-left: 10px; + margin-top: 5px; } .pad-right-10 { - padding-right: 10px; + padding-right: 10px; } .inline-block { - display: inline-block; + display: inline-block; } diff --git a/lib/GUI/entry.hbs b/lib/GUI/entry.hbs index 43a79c2bf4e9..6e63d37137d7 100644 --- a/lib/GUI/entry.hbs +++ b/lib/GUI/entry.hbs @@ -1,20 +1,20 @@
-
-
-

- {{ name }} - v{{ version }} -

-
-
-
- By: {{ _npmUser.name }} -
-
-
-
-
-

{{ description }}

-
-
+
+
+

+ {{ name }} + v{{ version }} +

+
+
+
+ By: {{ _npmUser.name }} +
+
+
+
+
+

{{ description }}

+
+
diff --git a/lib/GUI/index.hbs b/lib/GUI/index.hbs index bb08ed4cbaea..37730c494ca6 100644 --- a/lib/GUI/index.hbs +++ b/lib/GUI/index.hbs @@ -1,127 +1,127 @@ - - - {{ name }} + + + {{ name }} - - - - - -
- - -
-
- -
-
-
- - - - -
-
-
-
-
-
+ + + +
+
+ +
+
+
+ + + + +
+
+
+
+
+ -
- {{#each packages}} - {{> entry}} - {{/each}} +
+ {{#each packages}} + {{> entry}} + {{/each}} - {{#unless packages.length}} -
- No Packages
- Use npm publish -
- {{/unless}} -
+ {{#unless packages.length}} +
+ No Packages
+ Use npm publish +
+ {{/unless}} +
-
+
- + -
+
- - - + + + diff --git a/lib/GUI/js/entry.js b/lib/GUI/js/entry.js index 054fcdb8e2cd..75d83b86328d 100644 --- a/lib/GUI/js/entry.js +++ b/lib/GUI/js/entry.js @@ -1,71 +1,71 @@ -var $ = require('unopinionate').selector, - onClick = require('onclick'), - transitionComplete = require('transition-complete'); +var $ = require('unopinionate').selector +var onClick = require('onclick') +var transitionComplete = require('transition-complete') $(function() { - onClick('.entry .name', function() { - var $this = $(this), - $entry = $this.closest('.entry'); + onClick('.entry .name', function() { + var $this = $(this) + var $entry = $this.closest('.entry') - //Close entry - if($entry.hasClass('open')) { - $entry - .height($entry.outerHeight()) - .removeClass('open'); + if ($entry.hasClass('open')) { + // Close entry + $entry + .height($entry.outerHeight()) + .removeClass('open') - setTimeout(function() { - $entry.css('height', $entry.attr('data-height') + 'px'); - }, 0); + setTimeout(function() { + $entry.css('height', $entry.attr('data-height') + 'px') + }, 0) - transitionComplete(function() { - $entry.find('.readme').remove(); - $entry.css('height', 'auto'); - }); - } - //Open entry - else { - //Close open entries - $('.entry.open').each(function() { - var $entry = $(this); - $entry - .height($entry.outerHeight()) - .removeClass('open'); + transitionComplete(function() { + $entry.find('.readme').remove() + $entry.css('height', 'auto') + }) - setTimeout(function() { - $entry.css('height', $entry.attr('data-height') + 'px'); - }, 0); + } else { + // Open entry + $('.entry.open').each(function() { + // Close open entries + var $entry = $(this) + $entry + .height($entry.outerHeight()) + .removeClass('open') - transitionComplete(function() { - $entry.find('.readme').remove(); - $entry.css('height', 'auto'); - }); - }); + setTimeout(function() { + $entry.css('height', $entry.attr('data-height') + 'px') + }, 0) - //Add the open class - $entry.addClass('open'); + transitionComplete(function() { + $entry.find('.readme').remove() + $entry.css('height', 'auto') + }) + }) - //Explicitly set heights for transitions - var height = $entry.outerHeight(); - $entry - .attr('data-height', height) - .css('height', height); + // Add the open class + $entry.addClass('open') - //Get the data - $.ajax({ - url: '-/readme/'+$entry.attr('data-name')+'/'+$entry.attr('data-version'), - dataType: 'text', - success: function(html) { - var $readme = $("
") - .html(html) - .appendTo($entry); + // Explicitly set heights for transitions + var height = $entry.outerHeight() + $entry + .attr('data-height', height) + .css('height', height) - $entry.height(height + $readme.outerHeight()); + // Get the data + $.ajax({ + url: '-/readme/'+$entry.attr('data-name')+'/'+$entry.attr('data-version'), + dataType: 'text', + success: function(html) { + var $readme = $("
") + .html(html) + .appendTo($entry) - transitionComplete(function() { - $entry.css('height', 'auto'); - }); - } - }); - } - }); -}); + $entry.height(height + $readme.outerHeight()) + + transitionComplete(function() { + $entry.css('height', 'auto') + }) + } + }) + } + }) +}) diff --git a/lib/GUI/js/main.js b/lib/GUI/js/main.js index 5b913883bde7..eb5217414636 100644 --- a/lib/GUI/js/main.js +++ b/lib/GUI/js/main.js @@ -6,7 +6,8 @@ require('./bootstrap-modal') require('./search') require('./entry') +var $ = require('unopinionate').selector $(document).on('click', '.js-userLogoutBtn', function() { - $('#userLogoutForm').submit() - return false + $('#userLogoutForm').submit() + return false }) diff --git a/lib/GUI/js/search.js b/lib/GUI/js/search.js index b6b8c0a57fe1..45218dd51232 100644 --- a/lib/GUI/js/search.js +++ b/lib/GUI/js/search.js @@ -1,81 +1,79 @@ -var $ = require('unopinionate').selector, - template = require('../entry.hbs'), - onScroll = require('onscroll'); +var $ = require('unopinionate').selector +var template = require('../entry.hbs') +var onScroll = require('onscroll') $(function() { - 'use strict'; - - (function( window, document ) { - - var $form = $('#search-form'); - var $input = $form.find('input'); - var $body = $('body'); - var $clear = $form.find('.clear'); - var $searchResults = $("#search-results"); - var $pkgListing = $("#all-packages"); - var $searchBtn = $('.js-search-btn'); - var request; - var currentResults; - - var toggle = function( validQuery ) { - $searchResults.toggleClass( 'show', validQuery ); - $pkgListing.toggleClass( 'hide', validQuery ); - - - $searchBtn.find('i').toggleClass( 'icon-cancel', validQuery ); - $searchBtn.find('i').toggleClass( 'icon-search', !validQuery ); - }; - - $form.bind('submit keyup', function(e) { - var q, qBool; - - e.preventDefault(); - - q = $input.val(); - qBool = q !== ''; - - toggle( qBool ); - - if( !qBool ) { - if( request && typeof request.abort === 'function' ) { - request.abort(); - } - - currentResults = null; - $searchResults.html(''); - return; - } - - if( request && typeof request.abort === 'function' ) { - request.abort(); - } - - if( !currentResults ) { - $searchResults.html( "Spinner" ); - } - - request = $.getJSON('-/search/' + q, function( results ) { - currentResults = results; - - if( results.length > 0 ) { - var html = ''; - - $.each(results, function( i, entry ) { - html += template( entry ); - }); - - $searchResults.html(html); - } else { - $searchResults.html("
No Results
"); - } - }); - }); - - $( document ).on( 'click', '.icon-cancel', function( e ) { - e.preventDefault(); - $input.val(''); - $form.keyup(); - }); - - })( window, window.document ); -}); + ;(function(window, document) { + var $form = $('#search-form') + var $input = $form.find('input') + var $body = $('body') + var $clear = $form.find('.clear') + var $searchResults = $('#search-results') + var $pkgListing = $('#all-packages') + var $searchBtn = $('.js-search-btn') + var request + var currentResults + + var toggle = function(validQuery) { + $searchResults.toggleClass('show', validQuery) + $pkgListing.toggleClass('hide', validQuery) + + $searchBtn.find('i').toggleClass('icon-cancel', validQuery) + $searchBtn.find('i').toggleClass('icon-search', !validQuery) + } + + $form.bind('submit keyup', function(e) { + var q, qBool + + e.preventDefault() + + q = $input.val() + qBool = (q !== '') + + toggle(qBool) + + if (!qBool) { + if (request && typeof request.abort === 'function') { + request.abort() + } + + currentResults = null + $searchResults.html('') + return + } + + if (request && typeof request.abort === 'function') { + request.abort() + } + + if (!currentResults) { + $searchResults.html( + "Spinner") + } + + request = $.getJSON('-/search/' + q, function( results ) { + currentResults = results + + if (results.length > 0) { + var html = '' + + $.each(results, function(i, entry) { + html += template(entry) + }) + + $searchResults.html(html) + } else { + $searchResults.html( + "
No Results
") + } + }) + }) + + $(document).on('click', '.icon-cancel', function(e) { + e.preventDefault() + $input.val('') + $form.keyup() + }) + + })(window, window.document) +}) diff --git a/lib/auth.js b/lib/auth.js index 307e52045683..e1c7220019f6 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,218 +1,221 @@ -var Path = require('path') - , crypto = require('crypto') - , Error = require('http-errors') - , Logger = require('./logger') - , assert = require('assert') +var assert = require('assert') +var Crypto = require('crypto') +var Error = require('http-errors') +var Path = require('path') +var Logger = require('./logger') module.exports = Auth function Auth(config) { - if (!(this instanceof Auth)) return new Auth(config) - this.config = config - this.logger = Logger.logger.child({sub: 'auth'}) - var stuff = { - config: config, - logger: this.logger, - } - - if (config.users_file) { - if (!config.auth || !config.auth.htpasswd) { - // b/w compat - config.auth = config.auth || {} - config.auth.htpasswd = {file: config.users_file} - } - } - - this.plugins = Object.keys(config.auth || {}).map(function(p) { - var plugin, name - try { - name = 'sinopia-' + p - plugin = require(name) - } catch(x) { - try { - name = p - plugin = require(name) - } catch(x) {} - } - - if (plugin == null) { - throw Error('"' + p + '" auth plugin not found\n' - + 'try "npm install sinopia-' + p + '"') - } - - if (typeof(plugin) !== 'function') - throw Error('"' + name + '" doesn\'t look like a valid auth plugin') - - plugin = plugin(config.auth[p], stuff) - - if (plugin == null || typeof(plugin.authenticate) !== 'function') - throw Error('"' + name + '" doesn\'t look like a valid auth plugin') - - return plugin - }) - - this.plugins.unshift({ - authenticate: function(user, password, cb) { - if (config.users != null - && config.users[user] != null - && (crypto.createHash('sha1').update(password).digest('hex') - === config.users[user].password) - ) { - return cb(null, [ user ]) - } - - return cb() - }, - - adduser: function(user, password, cb) { - if (config.users && config.users[user]) - return cb(Error[403]('this user already exists')) - - return cb() - }, - }) - - this.plugins.push({ - authenticate: function(user, password, cb) { - return cb(Error[403]('bad username/password, access denied')) - }, - - adduser: function(user, password, cb) { - return cb(Error[409]('registration is disabled')) - }, - }) + var self = Object.create(Auth.prototype) + self.config = config + self.logger = Logger.logger.child({ sub: 'auth' }) + + var stuff = { + config: config, + logger: self.logger, + } + + if (config.users_file) { + if (!config.auth || !config.auth.htpasswd) { + // b/w compat + config.auth = config.auth || {} + config.auth.htpasswd = { file: config.users_file } + } + } + + self.plugins = Object.keys(config.auth || {}).map(function(p) { + var plugin, name + try { + name = 'sinopia-' + p + plugin = require(name) + } catch(x) { + try { + name = p + plugin = require(name) + } catch(x) {} + } + + if (plugin == null) { + throw Error('"' + p + '" auth plugin not found\n' + + 'try "npm install sinopia-' + p + '"') + } + + if (typeof(plugin) !== 'function') + throw Error('"' + name + '" doesn\'t look like a valid auth plugin') + + plugin = plugin(config.auth[p], stuff) + + if (plugin == null || typeof(plugin.authenticate) !== 'function') + throw Error('"' + name + '" doesn\'t look like a valid auth plugin') + + return plugin + }) + + self.plugins.unshift({ + authenticate: function(user, password, cb) { + if (config.users != null + && config.users[user] != null + && (Crypto.createHash('sha1').update(password).digest('hex') + === config.users[user].password) + ) { + return cb(null, [ user ]) + } + + return cb() + }, + + adduser: function(user, password, cb) { + if (config.users && config.users[user]) + return cb( Error[403]('self user already exists') ) + + return cb() + }, + }) + + self.plugins.push({ + authenticate: function(user, password, cb) { + return cb( Error[403]('bad username/password, access denied') ) + }, + + adduser: function(user, password, cb) { + return cb( Error[409]('registration is disabled') ) + }, + }) + + return self } Auth.prototype.authenticate = function(user, password, cb) { - var plugins = this.plugins.slice(0) - - !function next() { - var p = plugins.shift() - p.authenticate(user, password, function(err, groups) { - if (err || groups) return cb(err, groups) - next() - }) - }() + var plugins = this.plugins.slice(0) + + ;(function next() { + var p = plugins.shift() + p.authenticate(user, password, function(err, groups) { + if (err || groups) return cb(err, groups) + next() + }) + })() } Auth.prototype.add_user = function(user, password, cb) { - var plugins = this.plugins.slice(0) - - !function next() { - var p = plugins.shift() - var n = 'adduser' - if (typeof(p[n]) !== 'function') { - n = 'add_user' - } - if (typeof(p[n]) !== 'function') { - next() - } else { - p[n](user, password, function(err, ok) { - if (err || ok) return cb(err, ok) - next() - }) - } - }() + var plugins = this.plugins.slice(0) + + ;(function next() { + var p = plugins.shift() + var n = 'adduser' + if (typeof(p[n]) !== 'function') { + n = 'add_user' + } + if (typeof(p[n]) !== 'function') { + next() + } else { + p[n](user, password, function(err, ok) { + if (err || ok) return cb(err, ok) + next() + }) + } + })() } Auth.prototype.auth_middleware = function() { - var self = this - return function(req, res, _next) { - req.pause() - function next(err) { - req.resume() - // uncomment this to reject users with bad auth headers - //return _next.apply(null, arguments) - - // swallow error, user remains unauthorized - // set remoteUserError to indicate that user was attempting authentication - if (err) req.remote_user.error = err.message - return _next() - } - - if (req.remote_user != null && req.remote_user.name !== undefined) return next() - req.remote_user = AnonymousUser() - - var authorization = req.headers.authorization - if (authorization == null) return next() - - var parts = authorization.split(' ') - - if (parts.length !== 2) return next({ - status: 400, - message: 'bad authorization header', - }) - - var scheme = parts[0] - , credentials = new Buffer(parts[1], 'base64').toString() - , index = credentials.indexOf(':') - - if (scheme !== 'Basic' || index < 0) return next({ - status: 400, - message: 'bad authorization header', - }) - - var user = credentials.slice(0, index) - , pass = credentials.slice(index + 1) - - self.authenticate(user, pass, function(err, groups) { - if (!err && groups != null && groups != false) { - req.remote_user = AuthenticatedUser(user, groups) - next() - } else { - req.remote_user = AnonymousUser() - next(err) - } - }) - } + var self = this + return function(req, res, _next) { + req.pause() + function next(err) { + req.resume() + // uncomment this to reject users with bad auth headers + //return _next.apply(null, arguments) + + // swallow error, user remains unauthorized + // set remoteUserError to indicate that user was attempting authentication + if (err) req.remote_user.error = err.message + return _next() + } + + if (req.remote_user != null && req.remote_user.name !== undefined) + return next() + req.remote_user = AnonymousUser() + + var authorization = req.headers.authorization + if (authorization == null) return next() + + var parts = authorization.split(' ') + + if (parts.length !== 2) + return next( Error[400]('bad authorization header') ) + + var scheme = parts[0] + var credentials = Buffer(parts[1], 'base64').toString() + var index = credentials.indexOf(':') + + if (scheme !== 'Basic' || index < 0) + return next( Error[400]('bad authorization header') ) + + var user = credentials.slice(0, index) + var pass = credentials.slice(index + 1) + + self.authenticate(user, pass, function(err, groups) { + if (!err && groups != null && groups != false) { + req.remote_user = AuthenticatedUser(user, groups) + next() + } else { + req.remote_user = AnonymousUser() + next(err) + } + }) + } } Auth.prototype.cookie_middleware = function() { - var self = this - return function(req, res, _next) { - req.pause() - function next(err) { - req.resume() - return _next() - } - - if (req.remote_user != null && req.remote_user.name !== undefined) return next() - req.remote_user = AnonymousUser() - - var cookie = req.cookies.get('token') - if (cookie == null) return next() - - var credentials = new Buffer(cookie, 'base64').toString() - var index = credentials.indexOf(':') - - var user = credentials.slice(0, index) - var pass = credentials.slice(index + 1) - - self.authenticate(user, pass, function(err, groups) { - if (!err && groups != null && groups != false) { - req.remote_user = AuthenticatedUser(user, groups) - next() - } else { - req.remote_user = AnonymousUser() - next(err) - } - }) - } + var self = this + + return function(req, res, _next) { + req.pause() + function next(err) { + req.resume() + return _next() + } + + if (req.remote_user != null && req.remote_user.name !== undefined) + return next() + + req.remote_user = AnonymousUser() + + var cookie = req.cookies.get('token') + if (cookie == null) return next() + + var credentials = Buffer(cookie, 'base64').toString() + var index = credentials.indexOf(':') + + var user = credentials.slice(0, index) + var pass = credentials.slice(index + 1) + + self.authenticate(user, pass, function(err, groups) { + if (!err && groups != null && groups != false) { + req.remote_user = AuthenticatedUser(user, groups) + next() + } else { + req.remote_user = AnonymousUser() + next(err) + } + }) + } } function AnonymousUser() { - return { - name: undefined, - // groups without '@' are going to be deprecated eventually - groups: ['$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous'], - } + return { + name: undefined, + // groups without '@' are going to be deprecated eventually + groups: [ '$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous' ], + } } function AuthenticatedUser(name, groups) { - groups = groups.concat(['$all', '$authenticated', '@all', '@authenticated', 'all']) - return { - name: name, - groups: groups, - } + groups = groups.concat([ '$all', '$authenticated', '@all', '@authenticated', 'all' ]) + return { + name: name, + groups: groups, + } } diff --git a/lib/cli.js b/lib/cli.js index 1c1b89a85b59..81e01655aa81 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -3,147 +3,147 @@ /*eslint no-sync:0*/ if (process.getuid && process.getuid() === 0) { - global.console.error("Sinopia doesn't need superuser privileges. Don't run it under root.") + global.console.error("Sinopia doesn't need superuser privileges. Don't run it under root.") } process.title = 'sinopia' try { - // for debugging memory leaks - // totally optional - require('heapdump') -} catch(err){} + // for debugging memory leaks + // totally optional + require('heapdump') +} catch(err) {} var logger = require('./logger') logger.setup() // default setup -var pkg_file = '../package.yaml' - , fs = require('fs') - , yaml = require('js-yaml') - , commander = require('commander') - , server = require('./index') - , crypto = require('crypto') - , Path = require('path') - , pkg = yaml.safeLoad(fs.readFileSync(__dirname + '/' + pkg_file, 'utf8')) +var commander = require('commander') +var fs = require('fs') +var YAML = require('js-yaml') +var Path = require('path') +var server = require('./index') +var pkg_file = '../package.yaml' +var pkg = YAML.safeLoad(fs.readFileSync(__dirname+'/'+ pkg_file, 'utf8')) commander - .option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)') - .option('-c, --config ', 'use this configuration file (default: ./config.yaml)') - .version(pkg.version) - .parse(process.argv) + .option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)') + .option('-c, --config ', 'use this configuration file (default: ./config.yaml)') + .version(pkg.version) + .parse(process.argv) if (commander.args.length == 1 && !commander.config) { - // handling "sinopia [config]" case if "-c" is missing in commandline - commander.config = commander.args.pop() + // handling "sinopia [config]" case if "-c" is missing in commandline + commander.config = commander.args.pop() } if (commander.args.length != 0) { - commander.help() + commander.help() } var config, config_path, have_question try { - if (commander.config) { - config_path = commander.config - config = yaml.safeLoad(fs.readFileSync(config_path, 'utf8')) - } else { - config_path = './config.yaml' - try { - config = yaml.safeLoad(fs.readFileSync(config_path, 'utf8')) - } catch(err) { - var readline = require('readline') - var rl = readline.createInterface(process.stdin, process.stdout) - var timeout = setTimeout(function() { - global.console.log('I got tired waiting for an answer. Exitting...') - process.exit(1) - }, 20000) - - ;(function askUser() { - have_question = true - rl.question('Config file doesn\'t exist, create a new one? (Y/n) ', function(x) { - clearTimeout(timeout) - if (x[0] == 'Y' || x[0] == 'y' || x === '') { - rl.close() - - var created_config = require('../lib/config_gen')() - config = yaml.safeLoad(created_config.yaml) - write_config_banner(created_config, config) - fs.writeFileSync(config_path, created_config.yaml) - afterConfigLoad() - } else if (x[0] == 'N' || x[0] == 'n') { - rl.close() - global.console.log('So, you just accidentally run me in a wrong folder. Exitting...') - process.exit(1) - } else { - askUser() - } - }) - })() - } - } -} catch(err) { - logger.logger.fatal({file: config_path, err: err}, 'cannot open config file @{file}: @{!err.message}') - process.exit(1) + if (commander.config) { + config_path = commander.config + config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8')) + } else { + config_path = './config.yaml' + try { + config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8')) + } catch(err) { + var readline = require('readline') + var rl = readline.createInterface(process.stdin, process.stdout) + var timeout = setTimeout(function() { + global.console.log('I got tired waiting for an answer. Exitting...') + process.exit(1) + }, 20000) + + ;(function askUser() { + have_question = true + rl.question('Config file doesn\'t exist, create a new one? (Y/n) ', function(x) { + clearTimeout(timeout) + if (x[0] == 'Y' || x[0] == 'y' || x === '') { + rl.close() + + var created_config = require('../lib/config_gen')() + config = YAML.safeLoad(created_config.yaml) + write_config_banner(created_config, config) + fs.writeFileSync(config_path, created_config.yaml) + afterConfigLoad() + } else if (x[0] == 'N' || x[0] == 'n') { + rl.close() + global.console.log('So, you just accidentally run me in a wrong folder. Exitting...') + process.exit(1) + } else { + askUser() + } + }) + })() + } + } +} catch (err) { + logger.logger.fatal({ file: config_path, err: err }, 'cannot open config file @{file}: @{!err.message}') + process.exit(1) } if (!have_question) afterConfigLoad() function get_hostport() { - // command line || config file || default - var hostport = commander.listen || String(config.listen || '') || '4873' - - hostport = hostport.split(':') - if (hostport.length < 2) { - hostport = [undefined, hostport[0]] - } - if (hostport[0] == null) { - hostport[0] = 'localhost' - } - return hostport + // command line || config file || default + var hostport = commander.listen || String(config.listen || '') || '4873' + + hostport = hostport.split(':') + if (hostport.length < 2) { + hostport = [ undefined, hostport[0] ] + } + if (hostport[0] == null) { + hostport[0] = 'localhost' + } + return hostport } function afterConfigLoad() { - if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version - if (!config.self_path) config.self_path = Path.resolve(config_path) + if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version + if (!config.self_path) config.self_path = Path.resolve(config_path) - logger.setup(config.logs) + logger.setup(config.logs) - var hostport = get_hostport() - server(config) - .listen(hostport[1], hostport[0]) - .on('error', function(err) { - logger.logger.fatal({err: err}, 'cannot create server: @{err.message}') - process.exit(2) - }) + var hostport = get_hostport() + server(config) + .listen(hostport[1], hostport[0]) + .on('error', function(err) { + logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}') + process.exit(2) + }) - logger.logger.warn({addr: 'http://'+hostport[0]+':'+hostport[1]+'/', version: 'Sinopia/'+pkg.version}, 'Server is listening on @{addr}') + logger.logger.warn({ addr: 'http://'+hostport[0]+':'+hostport[1]+'/', version: 'Sinopia/'+pkg.version }, 'Server is listening on @{addr}') - // undocumented stuff for tests - if (typeof(process.send) === 'function') { - process.send({sinopia_started: hostport}) - } + // undocumented stuff for tests + if (typeof(process.send) === 'function') { + process.send({ sinopia_started: hostport }) + } } function write_config_banner(def, config) { - var hostport = get_hostport() - var log = global.console.log - - log('===========================================================') - log(' Creating a new configuration file: "%s"', config_path) - log(' ') - log(' If you want to setup npm to work with this registry,') - log(' run following commands:') - log(' ') - log(' $ npm set registry http://%s:%s/', hostport[0], hostport[1]) - log(' $ npm set always-auth true') - log(' $ npm adduser') - log(' Username: %s', def.user) - log(' Password: %s', def.pass) - log('===========================================================') + var hostport = get_hostport() + var log = global.console.log + + log('===========================================================') + log(' Creating a new configuration file: "%s"', config_path) + log(' ') + log(' If you want to setup npm to work with this registry,') + log(' run following commands:') + log(' ') + log(' $ npm set registry http://%s:%s/', hostport[0], hostport[1]) + log(' $ npm set always-auth true') + log(' $ npm adduser') + log(' Username: %s', def.user) + log(' Password: %s', def.pass) + log('===========================================================') } process.on('uncaughtException', function(err) { - logger.logger.fatal({err: err}, 'uncaught exception, please report this\n@{err.stack}') - process.exit(255) + logger.logger.fatal( { err: err } + , 'uncaught exception, please report this\n@{err.stack}' ) + process.exit(255) }) diff --git a/lib/config.js b/lib/config.js index d9687f4f452b..4666b227da96 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,187 +1,189 @@ -var assert = require('assert') - , crypto = require('crypto') - , Path = require('path') - , minimatch = require('minimatch') - , Error = require('http-errors') - , LocalList = require('./local-list') - , utils = require('./utils') +var assert = require('assert') +var Crypto = require('crypto') +var Error = require('http-errors') +var minimatch = require('minimatch') +var Path = require('path') +var LocalList = require('./local-list') +var Utils = require('./utils') // [[a, [b, c]], d] -> [a, b, c, d] function flatten(array) { - var result = [] - for (var i=0; i= last_suffix - || (m[4] === '' && last_suffix !== Infinity)) { - throw new Error('invalid interval: ' + interval) - } - last_suffix = parse_interval_table[m[4]] - result += Number(m[1]) * parse_interval_table[m[4]] - }) - return result + if (typeof(interval) === 'number') return interval * 1000 + + var result = 0 + var last_suffix = Infinity + interval.split(/\s+/).forEach(function(x) { + if (!x) return + var m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/) + if (!m + || parse_interval_table[m[4]] >= last_suffix + || (m[4] === '' && last_suffix !== Infinity)) { + throw Error('invalid interval: ' + interval) + } + last_suffix = parse_interval_table[m[4]] + result += Number(m[1]) * parse_interval_table[m[4]] + }) + return result } diff --git a/lib/config_gen.js b/lib/config_gen.js index 420ff3f7bc06..b01eed318c6d 100644 --- a/lib/config_gen.js +++ b/lib/config_gen.js @@ -1,19 +1,19 @@ -var fs = require('fs') - , crypto = require('crypto') +var Crypto = require('crypto') +var fs = require('fs') module.exports = function create_config() { - var pass = crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, '') - , pass_digest = crypto.createHash('sha1').update(pass).digest('hex') + var pass = Crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, '') + var pass_digest = Crypto.createHash('sha1').update(pass).digest('hex') - /*eslint no-sync:0*/ - var config = fs.readFileSync(require.resolve('./config_def.yaml'), 'utf8') + /*eslint no-sync:0*/ + var config = fs.readFileSync(require.resolve('./config_def.yaml'), 'utf8') - config = config.replace('__PASSWORD__', pass_digest) + config = config.replace('__PASSWORD__', pass_digest) - return { - yaml: config, - user: 'admin', - pass: pass, - } + return { + yaml: config, + user: 'admin', + pass: pass, + } } diff --git a/lib/index-web.js b/lib/index-web.js index b8b1b7f6e525..a1db7e22040f 100644 --- a/lib/index-web.js +++ b/lib/index-web.js @@ -1,112 +1,113 @@ -var fs = require('fs') -var marked = require('marked') -var search = require('./search') +var Cookies = require('cookies') +var express = require('express') +var fs = require('fs') +var marked = require('marked') var Handlebars = require('handlebars') -var Error = require('http-errors') -var express = require('express') -var Cookies = require('cookies') +var Error = require('http-errors') +var Search = require('./search') module.exports = function(config, auth, storage) { - var app = express() - app.use(Cookies.express()) - app.use(express.urlencoded()) - app.use(auth.cookie_middleware()) - app.use(function(req, res, next) { - // disable loading in frames (clickjacking, etc.) - res.header('X-Frame-Options', 'deny') - next() - }) - - search.configureStorage(storage) - - Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8')); - var template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8')); - - app.get('/', function(req, res, next) { - var base = config.url_prefix || req.protocol + '://' + req.get('host') - res.setHeader('Content-Type', 'text/html'); - - storage.get_local(function(err, packages) { - res.send(template({ - name: config.web.title || "Sinopia", - packages: packages, - baseUrl: base, - username: req.remote_user.name, - })); - }); - }); - - // Static - app.get('/-/static/:filename', function(req, res, next) { - var file = __dirname + '/static/' + req.params.filename - fs.exists(file, function(exists) { - if (exists) { - res.sendfile(file) - } else { - res.status(404); - res.send("File Not Found") - } - }) - }) - - app.get('/-/logo', function(req, res, next) { - res.sendfile(config.web.logo ? config.web.logo : __dirname + "/static/logo.png") - }) - - app.get('/-/logo-sm', function(req, res, next) { - res.sendfile(config.web.logosm ? config.web.logosm : __dirname + "/static/logo-sm.png") - }) - - app.post('/-/login', function(req, res, next) { - var base = config.url_prefix || req.protocol + '://' + req.get('host') - res.cookies.set('token', Buffer(req.body.user + ':' + req.body.pass).toString('base64')) - res.redirect(base) - }) - - app.post('/-/logout', function(req, res, next) { - var base = config.url_prefix || req.protocol + '://' + req.get('host') - res.cookies.set('token', '') - res.redirect(base) - }) - - // Search - app.get('/-/search/:anything', function(req, res, next) { - var results = search.query(req.params.anything), - packages = [] - - var getData = function(i) { - storage.get_package(results[i].ref, function(err, entry) { - if (entry) { - packages.push(entry.versions[entry['dist-tags'].latest]) - } - - if (i >= results.length - 1) { - res.send(packages) - } else { - getData(i + 1) - } - }) - } - - if (results.length) { - getData(0); - } else { - res.send([]) - } - }) - - // Readme - marked.setOptions({ - highlight: function (code) { - return require('highlight.js').highlightAuto(code).value - } - }) - - app.get('/-/readme/:package/:version?', function(req, res, next) { - storage.get_package(req.params.package, {req: req}, function(err, info) { - if (err) return next(err) - res.send(marked(info.readme || 'ERROR: No README data found!')) - }) - }) - return app + var app = express() + app.use(Cookies.express()) + app.use(express.urlencoded()) + app.use(auth.cookie_middleware()) + app.use(function(req, res, next) { + // disable loading in frames (clickjacking, etc.) + res.header('X-Frame-Options', 'deny') + next() + }) + + Search.configureStorage(storage) + + Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8')) + var template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8')) + + app.get('/', function(req, res, next) { + var base = config.url_prefix || req.protocol + '://' + req.get('host') + res.setHeader('Content-Type', 'text/html') + + storage.get_local(function(err, packages) { + if (err) throw err // that function shouldn't produce any + res.send(template({ + name: config.web.title || 'Sinopia', + packages: packages, + baseUrl: base, + username: req.remote_user.name, + })) + }) + }) + + // Static + app.get('/-/static/:filename', function(req, res, next) { + var file = __dirname + '/static/' + req.params.filename + fs.exists(file, function(exists) { + if (exists) { + res.sendfile(file) + } else { + next( Error[404]('File Not Found') ) + } + }) + }) + + app.get('/-/logo', function(req, res, next) { + res.sendfile(config.web.logo ? config.web.logo : __dirname + '/static/logo.png') + }) + + app.get('/-/logo-sm', function(req, res, next) { + res.sendfile(config.web.logosm ? config.web.logosm : __dirname + '/static/logo-sm.png') + }) + + app.post('/-/login', function(req, res, next) { + var base = config.url_prefix || req.protocol + '://' + req.get('host') + res.cookies.set('token', Buffer(req.body.user + ':' + req.body.pass).toString('base64')) + res.redirect(base) + }) + + app.post('/-/logout', function(req, res, next) { + var base = config.url_prefix || req.protocol + '://' + req.get('host') + res.cookies.set('token', '') + res.redirect(base) + }) + + // Search + app.get('/-/search/:anything', function(req, res, next) { + var results = Search.query(req.params.anything), + packages = [] + + var getData = function(i) { + storage.get_package(results[i].ref, function(err, entry) { + if (!err && entry) { + packages.push(entry.versions[entry['dist-tags'].latest]) + } + + if (i >= results.length - 1) { + res.send(packages) + } else { + getData(i + 1) + } + }) + } + + if (results.length) { + getData(0) + } else { + res.send([]) + } + }) + + // Readme + marked.setOptions({ + highlight: function (code) { + return require('highlight.js').highlightAuto(code).value + } + }) + + app.get('/-/readme/:package/:version?', function(req, res, next) { + storage.get_package(req.params.package, {req: req}, function(err, info) { + if (err) return next(err) + res.send( marked(info.readme || 'ERROR: No README data found!') ) + }) + }) + return app } + diff --git a/lib/index.js b/lib/index.js index 41c03cd0f050..8876af73ee6a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,433 +1,416 @@ -var express = require('express') - , cookies = require('cookies') - , utils = require('./utils') - , Storage = require('./storage') - , Config = require('./config') - , Error = require('http-errors') - , Middleware = require('./middleware') - , Logger = require('./logger') - , Cats = require('./status-cats') - , validate_name = Middleware.validate_name - , media = Middleware.media - , expect_json = Middleware.expect_json - , fs = require('fs') - , Auth = require('./auth') +var Cookies = require('cookies') +var express = require('express') +var fs = require('fs') +var Error = require('http-errors') +var Auth = require('./auth') +var Logger = require('./logger') +var Config = require('./config') +var Middleware = require('./middleware') +var Cats = require('./status-cats') +var Storage = require('./storage') +var Utils = require('./utils') +var expect_json = Middleware.expect_json +var media = Middleware.media +var validate_name = Middleware.validate_name function match(regexp) { - return function(req, res, next, value, name) { - if (regexp.exec(value)) { - next() - } else { - next('route') - } - } + return function(req, res, next, value, name) { + if (regexp.exec(value)) { + next() + } else { + next('route') + } + } } module.exports = function(config_hash) { - var config = new Config(config_hash) - , storage = new Storage(config) - , auth = new Auth(config) - - var can = function(action) { - return function(req, res, next) { - if (config['allow_'+action](req.params.package, req.remote_user)) { - next() - } else { - if (!req.remote_user.name) { - if (req.remote_user.error) { - var message = "can't "+action+' restricted package, ' + req.remote_user.error - } else { - var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?" - } - next(Error[403](message)) - } else { - next(Error[403]('user ' + req.remote_user.name - + ' not allowed to ' + action + ' it')) - } - } - } - } - - var app = express() - - // run in production mode by default, just in case - // it shouldn't make any difference anyway - app.set('env', process.env.NODE_ENV || 'production') - - function error_reporting_middleware(req, res, next) { - res.report_error = res.report_error || function(err) { - if (err.status && err.status >= 400 && err.status < 600) { - if (!res.headersSent) { - res.status(err.status) - res.send({error: err.message || 'unknown error'}) - } - } else { - Logger.logger.error({err: err}, 'unexpected error: @{!err.message}\n@{err.stack}') - if (!res.status || !res.send) { - Logger.logger.error('this is an error in express.js, please report this') - res.destroy() - } else if (!res.headersSent) { - res.status(500) - res.send({error: 'internal server error'}) - } else { - // socket should be already closed - } - } - } - next() - } - - app.use(error_reporting_middleware) - app.use(Middleware.log_and_etagify) - app.use(function(req, res, next) { - res.setHeader('X-Powered-By', config.user_agent) - next() - }) - app.use(Cats.middleware) - app.use(auth.auth_middleware()) - app.use(express.json({strict: false, limit: config.max_body_size || '10mb'})) - app.use(express.compress()) - app.use(Middleware.anti_loop(config)) - - // validate all of these params as a package name - // this might be too harsh, so ask if it causes trouble - app.param('package', validate_name) - app.param('filename', validate_name) - app.param('tag', validate_name) - app.param('version', validate_name) - app.param('revision', validate_name) - - // these can't be safely put into express url for some reason - app.param('_rev', match(/^-rev$/)) - app.param('org_couchdb_user', match(/^org\.couchdb\.user:/)) - app.param('anything', match(/.*/)) + var config = Config(config_hash) + var storage = Storage(config) + var auth = Auth(config) + + var can = function(action) { + return function(req, res, next) { + if (config['allow_'+action](req.params.package, req.remote_user)) { + next() + } else { + if (!req.remote_user.name) { + if (req.remote_user.error) { + var message = "can't "+action+' restricted package, ' + req.remote_user.error + } else { + var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?" + } + next( Error[403](message) ) + } else { + next( Error[403]('user ' + req.remote_user.name + + ' not allowed to ' + action + ' it') ) + } + } + } + } + + var app = express() + + // run in production mode by default, just in case + // it shouldn't make any difference anyway + app.set('env', process.env.NODE_ENV || 'production') + + function error_reporting_middleware(req, res, next) { + res.report_error = res.report_error || function(err) { + if (err.status && err.status >= 400 && err.status < 600) { + if (!res.headersSent) { + res.status(err.status) + res.send({ error: err.message || 'unknown error' }) + } + } else { + Logger.logger.error( { err: err } + , 'unexpected error: @{!err.message}\n@{err.stack}') + if (!res.status || !res.send) { + Logger.logger.error('this is an error in express.js, please report this') + res.destroy() + } else if (!res.headersSent) { + res.status(500) + res.send({ error: 'internal server error' }) + } else { + // socket should be already closed + } + } + } + next() + } + + app.use(error_reporting_middleware) + app.use(Middleware.log_and_etagify) + app.use(function(req, res, next) { + res.setHeader('X-Powered-By', config.user_agent) + next() + }) + app.use(Cats.middleware) + app.use(auth.auth_middleware()) + app.use(express.json({ strict: false, limit: config.max_body_size || '10mb' })) + app.use(express.compress()) + app.use(Middleware.anti_loop(config)) + + // validate all of these params as a package name + // this might be too harsh, so ask if it causes trouble + app.param('package', validate_name) + app.param('filename', validate_name) + app.param('tag', validate_name) + app.param('version', validate_name) + app.param('revision', validate_name) + + // these can't be safely put into express url for some reason + app.param('_rev', match(/^-rev$/)) + app.param('org_couchdb_user', match(/^org\.couchdb\.user:/)) + app.param('anything', match(/.*/)) /* app.get('/-/all', function(req, res) { - var https = require('https') - var JSONStream = require('JSONStream') - var request = require('request')({ - url: 'https://registry.npmjs.org/-/all', - }) - .pipe(JSONStream.parse('*')) - .on('data', function(d) { - console.log(d) - }) - })*/ - - // TODO: anonymous user? - app.get('/:package/:version?', can('access'), function(req, res, next) { - storage.get_package(req.params.package, {req: req}, function(err, info) { - if (err) return next(err) - info = utils.filter_tarball_urls(info, req, config) - - var version = req.params.version - , t - if (!version) { - return res.send(info) - } - - if ((t = utils.get_version(info, version)) != null) { - return res.send(t) - } - - if (info['dist-tags'] != null) { - if (info['dist-tags'][version] != null) { - version = info['dist-tags'][version] - if ((t = utils.get_version(info, version)) != null) { - return res.send(t) - } - } - } - - return next(Error[404]('version not found: ' + req.params.version)) - }) - }) - - app.get('/:package/-/:filename', can('access'), function(req, res, next) { - var stream = storage.get_tarball(req.params.package, req.params.filename) - stream.on('content-length', function(v) { - res.header('Content-Length', v) - }) - stream.on('error', function(err) { - return res.report_error(err) - }) - res.header('Content-Type', 'application/octet-stream') - stream.pipe(res) - }) - - // searching packages - app.get('/-/all/:anything?', function(req, res, next) { - storage.search(req.param.startkey || 0, {req: req}, function(err, result) { - if (err) return next(err) - for (var pkg in result) { - if (!config.allow_access(pkg, req.remote_user)) { - delete result[pkg] - } - } - return res.send(result) - }) - }) - - //app.get('/*', function(req, res) { - // proxy.request(req, res) - //}) - - // placeholder 'cause npm require to be authenticated to publish - // we do not do any real authentication yet - app.post('/_session', cookies.express(), function(req, res) { - res.cookies.set('AuthSession', String(Math.random()), { - // npmjs.org sets 10h expire - expires: new Date(Date.now() + 10*60*60*1000) - }) - res.send({'ok':true,'name':'somebody','roles':[]}) - }) - - app.get('/-/user/:org_couchdb_user', function(req, res, next) { - res.status(200) - return res.send({ - ok: 'you are authenticated as "' + req.remote_user.name + '"', - }) - }) - - app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) { - if (req.remote_user.name != null) { - res.status(201) - return res.send({ - ok: 'you are authenticated as "' + req.remote_user.name + '"', - }) - } else { - if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') { - return next(Error[400]('user/password is not found in request (npm issue?)')) - } - auth.add_user(req.body.name, req.body.password, function(err) { - if (err) { - if (err.status < 500 && err.message === 'this user already exists') { - // with npm registering is the same as logging in - // so we replace message in case of conflict - return next(Error[409]('bad username/password, access denied')) - } - return next(err) - } - - res.status(201) - return res.send({ - ok: 'user "' + req.body.name + '" created', - }) - }) - } - }) - - // tagging a package - app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) { - if (typeof(req.body) !== 'string') return next('route') - - var tags = {} - tags[req.params.tag] = req.body - storage.add_tags(req.params.package, tags, function(err) { - if (err) return next(err) - res.status(201) - return res.send({ - ok: 'package tagged' - }) - }) - }) - - // publishing a package - app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) { - var name = req.params.package - - if (Object.keys(req.body).length == 1 && utils.is_object(req.body.users)) { - // 501 status is more meaningful, but npm doesn't show error message for 5xx - return next(Error[404]('npm star|unstar calls are not implemented')) - } - - try { - var metadata = utils.validate_metadata(req.body, name) - } catch(err) { - return next(Error[422]('bad incoming package data')) - } - - if (req.params._rev) { - storage.change_package(name, metadata, req.params.revision, function(err) { - after_change(err, 'package changed') - }) - } else { - storage.add_package(name, metadata, function(err) { - after_change(err, 'created new package') - }) - } - - function after_change(err, ok_message) { - // old npm behaviour - if (metadata._attachments == null) { - if (err) return next(err) - res.status(201) - return res.send({ - ok: ok_message - }) - } - - // npm-registry-client 0.3+ embeds tarball into the json upload - // https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0 - // issue #31, dealing with it here: - - if (typeof(metadata._attachments) != 'object' - || Object.keys(metadata._attachments).length != 1 - || typeof(metadata.versions) != 'object' - || Object.keys(metadata.versions).length != 1) { - - // npm is doing something strange again - // if this happens in normal circumstances, report it as a bug - return next(Error[400]('unsupported registry call')) - } - - if (err && err.status != 409) return next(err) - - // at this point document is either created or existed before - var t1 = Object.keys(metadata._attachments)[0] - create_tarball(t1, metadata._attachments[t1], function(err) { - if (err) return next(err) - - var t2 = Object.keys(metadata.versions)[0] - metadata.versions[t2].readme = metadata.readme != null ? String(metadata.readme) : '' - create_version(t2, metadata.versions[t2], function(err) { - if (err) return next(err) - - add_tags(metadata['dist-tags'], function(err) { - if (err) return next(err) - - res.status(201) - return res.send({ - ok: ok_message - }) - }) - }) - }) - } - - function create_tarball(filename, data, cb) { - var stream = storage.add_tarball(name, filename) - stream.on('error', function(err) { - cb(err) - }) - stream.on('success', function() { - cb() - }) - - // this is dumb and memory-consuming, but what choices do we have? - stream.end(new Buffer(data.data, 'base64')) - stream.done() - } - - function create_version(version, data, cb) { - storage.add_version(name, version, data, null, cb) - } - - function add_tags(tags, cb) { - storage.add_tags(name, tags, cb) - } - }) - - // unpublishing an entire package - app.delete('/:package/-rev/*', can('publish'), function(req, res, next) { - storage.remove_package(req.params.package, function(err) { - if (err) return next(err) - res.status(201) - return res.send({ - ok: 'package removed' - }) - }) - }) - - // removing a tarball - app.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req, res, next) { - storage.remove_tarball(req.params.package, req.params.filename, req.params.revision, function(err) { - if (err) return next(err) - res.status(201) - return res.send({ - ok: 'tarball removed' - }) - }) - }) - - // uploading package tarball - app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) { - var name = req.params.package - - var stream = storage.add_tarball(name, req.params.filename) - req.pipe(stream) - - // checking if end event came before closing - var complete = false - req.on('end', function() { - complete = true - stream.done() - }) - req.on('close', function() { - if (!complete) { - stream.abort() - } - }) - - stream.on('error', function(err) { - return res.report_error(err) - }) - stream.on('success', function() { - res.status(201) - return res.send({ - ok: 'tarball uploaded successfully' - }) - }) - }) - - // adding a version - app.put('/:package/:version/-tag/:tag', can('publish'), media('application/json'), expect_json, function(req, res, next) { - var name = req.params.package - , version = req.params.version - , tag = req.params.tag - - storage.add_version(name, version, req.body, tag, function(err) { - if (err) return next(err) - res.status(201) - return res.send({ - ok: 'package published' - }) - }) - }) - - // hook for tests only - if (config._debug) { - app.get('/-/_debug', function(req, res) { - var do_gc = typeof(global.gc) !== 'undefined' - if (do_gc) global.gc() - res.send({ - pid: process.pid, - main: process.mainModule.filename, - conf: config.self_path, - mem: process.memoryUsage(), - gc: do_gc, - }) - }) - } - - app.use(app.router) - app.use(function(err, req, res, next) { - if (typeof(res.report_error) !== 'function') { - // in case of very early error this middleware may not be loaded before error is generated - // fixing that - error_reporting_middleware(req, res, function(){}) - } - res.report_error(err) - }) - - if (config.web && config.web.enable) { - app.use(require('./index-web')(config, auth, storage)) - } else { - app.get('/', function(req, res) { - res.send('Web interface is disabled in the config file') - }) - } - - return app + var https = require('https') + var JSONStream = require('JSONStream') + var request = require('request')({ + url: 'https://registry.npmjs.org/-/all', + }) + .pipe(JSONStream.parse('*')) + .on('data', function(d) { + console.log(d) + }) + })*/ + + // TODO: anonymous user? + app.get('/:package/:version?', can('access'), function(req, res, next) { + storage.get_package(req.params.package, { req: req }, function(err, info) { + if (err) return next(err) + info = Utils.filter_tarball_urls(info, req, config) + + var version = req.params.version + if (!version) return res.send(info) + + var t = Utils.get_version(info, version) + if (t != null) return res.send(t) + + if (info['dist-tags'] != null) { + if (info['dist-tags'][version] != null) { + version = info['dist-tags'][version] + if ((t = Utils.get_version(info, version)) != null) { + return res.send(t) + } + } + } + + return next( Error[404]('version not found: ' + req.params.version) ) + }) + }) + + app.get('/:package/-/:filename', can('access'), function(req, res, next) { + var stream = storage.get_tarball(req.params.package, req.params.filename) + stream.on('content-length', function(v) { + res.header('Content-Length', v) + }) + stream.on('error', function(err) { + return res.report_error(err) + }) + res.header('Content-Type', 'application/octet-stream') + stream.pipe(res) + }) + + // searching packages + app.get('/-/all/:anything?', function(req, res, next) { + storage.search(req.param.startkey || 0, {req: req}, function(err, result) { + if (err) return next(err) + for (var pkg in result) { + if (!config.allow_access(pkg, req.remote_user)) { + delete result[pkg] + } + } + return res.send(result) + }) + }) + + //app.get('/*', function(req, res) { + // proxy.request(req, res) + //}) + + // placeholder 'cause npm require to be authenticated to publish + // we do not do any real authentication yet + app.post('/_session', Cookies.express(), function(req, res) { + res.cookies.set('AuthSession', String(Math.random()), { + // npmjs.org sets 10h expire + expires: new Date(Date.now() + 10*60*60*1000) + }) + res.send({ ok: true, name: 'somebody', roles: [] }) + }) + + app.get('/-/user/:org_couchdb_user', function(req, res, next) { + res.status(200) + return res.send({ + ok: 'you are authenticated as "' + req.remote_user.name + '"', + }) + }) + + app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) { + if (req.remote_user.name != null) { + res.status(201) + return res.send({ + ok: 'you are authenticated as "' + req.remote_user.name + '"', + }) + } else { + if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') { + return next( Error[400]('user/password is not found in request (npm issue?)') ) + } + auth.add_user(req.body.name, req.body.password, function(err) { + if (err) { + if (err.status < 500 && err.message === 'this user already exists') { + // with npm registering is the same as logging in + // so we replace message in case of conflict + return next( Error[409]('bad username/password, access denied') ) + } + return next(err) + } + + res.status(201) + return res.send({ ok: 'user "' + req.body.name + '" created' }) + }) + } + }) + + // tagging a package + app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) { + if (typeof(req.body) !== 'string') return next('route') + + var tags = {} + tags[req.params.tag] = req.body + storage.add_tags(req.params.package, tags, function(err) { + if (err) return next(err) + res.status(201) + return res.send({ ok: 'package tagged' }) + }) + }) + + // publishing a package + app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) { + var name = req.params.package + + if (Object.keys(req.body).length == 1 && Utils.is_object(req.body.users)) { + // 501 status is more meaningful, but npm doesn't show error message for 5xx + return next( Error[404]('npm star|unstar calls are not implemented') ) + } + + try { + var metadata = Utils.validate_metadata(req.body, name) + } catch(err) { + return next( Error[422]('bad incoming package data') ) + } + + if (req.params._rev) { + storage.change_package(name, metadata, req.params.revision, function(err) { + after_change(err, 'package changed') + }) + } else { + storage.add_package(name, metadata, function(err) { + after_change(err, 'created new package') + }) + } + + function after_change(err, ok_message) { + // old npm behaviour + if (metadata._attachments == null) { + if (err) return next(err) + res.status(201) + return res.send({ ok: ok_message }) + } + + // npm-registry-client 0.3+ embeds tarball into the json upload + // https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0 + // issue #31, dealing with it here: + + if (typeof(metadata._attachments) !== 'object' + || Object.keys(metadata._attachments).length !== 1 + || typeof(metadata.versions) !== 'object' + || Object.keys(metadata.versions).length !== 1) { + + // npm is doing something strange again + // if this happens in normal circumstances, report it as a bug + return next( Error[400]('unsupported registry call') ) + } + + if (err && err.status != 409) return next(err) + + // at this point document is either created or existed before + var t1 = Object.keys(metadata._attachments)[0] + create_tarball(t1, metadata._attachments[t1], function(err) { + if (err) return next(err) + + var t2 = Object.keys(metadata.versions)[0] + metadata.versions[t2].readme = metadata.readme != null ? String(metadata.readme) : '' + create_version(t2, metadata.versions[t2], function(err) { + if (err) return next(err) + + add_tags(metadata['dist-tags'], function(err) { + if (err) return next(err) + + res.status(201) + return res.send({ ok: ok_message }) + }) + }) + }) + } + + function create_tarball(filename, data, cb) { + var stream = storage.add_tarball(name, filename) + stream.on('error', function(err) { + cb(err) + }) + stream.on('success', function() { + cb() + }) + + // this is dumb and memory-consuming, but what choices do we have? + stream.end(Buffer(data.data, 'base64')) + stream.done() + } + + function create_version(version, data, cb) { + storage.add_version(name, version, data, null, cb) + } + + function add_tags(tags, cb) { + storage.add_tags(name, tags, cb) + } + }) + + // unpublishing an entire package + app.delete('/:package/-rev/*', can('publish'), function(req, res, next) { + storage.remove_package(req.params.package, function(err) { + if (err) return next(err) + res.status(201) + return res.send({ ok: 'package removed' }) + }) + }) + + // removing a tarball + app.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req, res, next) { + storage.remove_tarball(req.params.package, req.params.filename, req.params.revision, function(err) { + if (err) return next(err) + res.status(201) + return res.send({ ok: 'tarball removed' }) + }) + }) + + // uploading package tarball + app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) { + var name = req.params.package + + var stream = storage.add_tarball(name, req.params.filename) + req.pipe(stream) + + // checking if end event came before closing + var complete = false + req.on('end', function() { + complete = true + stream.done() + }) + req.on('close', function() { + if (!complete) { + stream.abort() + } + }) + + stream.on('error', function(err) { + return res.report_error(err) + }) + stream.on('success', function() { + res.status(201) + return res.send({ + ok: 'tarball uploaded successfully' + }) + }) + }) + + // adding a version + app.put('/:package/:version/-tag/:tag', can('publish'), media('application/json'), expect_json, function(req, res, next) { + var name = req.params.package + var version = req.params.version + var tag = req.params.tag + + storage.add_version(name, version, req.body, tag, function(err) { + if (err) return next(err) + res.status(201) + return res.send({ ok: 'package published' }) + }) + }) + + // hook for tests only + if (config._debug) { + app.get('/-/_debug', function(req, res) { + var do_gc = typeof(global.gc) !== 'undefined' + if (do_gc) global.gc() + res.send({ + pid : process.pid, + main : process.mainModule.filename, + conf : config.self_path, + mem : process.memoryUsage(), + gc : do_gc, + }) + }) + } + + app.use(app.router) + app.use(function(err, req, res, next) { + if (typeof(res.report_error) !== 'function') { + // in case of very early error this middleware may not be loaded before error is generated + // fixing that + error_reporting_middleware(req, res, function(){}) + } + res.report_error(err) + }) + + if (config.web && config.web.enable) { + app.use(require('./index-web')(config, auth, storage)) + } else { + app.get('/', function(req, res) { + res.send('Web interface is disabled in the config file') + }) + } + + return app } diff --git a/lib/local-fs.js b/lib/local-fs.js index 714b32819cae..db85b00068ef 100644 --- a/lib/local-fs.js +++ b/lib/local-fs.js @@ -1,270 +1,270 @@ -var fs = require('fs') - , Path = require('path') - , mkdirp = require('mkdirp') - , mystreams = require('./streams') - , Error = require('http-errors') +var fs = require('fs') +var Error = require('http-errors') +var mkdirp = require('mkdirp') +var Path = require('path') +var MyStreams = require('./streams') function FSError(code) { - var err = Error(code) - err.code = code - return err + var err = Error(code) + err.code = code + return err } try { - var fsExt = require('fs-ext') -} catch(e) { - fsExt = { - flock: function() { - arguments[arguments.length-1]() - } - } + var fsExt = require('fs-ext') +} catch (e) { + fsExt = { + flock: function() { + arguments[arguments.length-1]() + } + } } function tempFile(str) { - return str + '.tmp' + String(Math.random()).substr(2) + return str + '.tmp' + String(Math.random()).substr(2) } function renameTmp(src, dst, _cb) { - function cb(err) { - if (err) fs.unlink(src) - _cb(err) - } - - if (process.platform !== 'win32') { - return fs.rename(src, dst, cb) - } - - // windows can't remove opened file, - // but it seem to be able to rename it - var tmp = tempFile(dst) - fs.rename(dst, tmp, function(err) { - fs.rename(src, dst, cb) - if (!err) fs.unlink(tmp) - }) + function cb(err) { + if (err) fs.unlink(src) + _cb(err) + } + + if (process.platform !== 'win32') { + return fs.rename(src, dst, cb) + } + + // windows can't remove opened file, + // but it seem to be able to rename it + var tmp = tempFile(dst) + fs.rename(dst, tmp, function(err) { + fs.rename(src, dst, cb) + if (!err) fs.unlink(tmp) + }) } function write(dest, data, cb) { - var safe_write = function(cb) { - var tmpname = tempFile(dest) - fs.writeFile(tmpname, data, function(err) { - if (err) return cb(err) - renameTmp(tmpname, dest, cb) - }) - } - - safe_write(function(err) { - if (err && err.code === 'ENOENT') { - mkdirp(Path.dirname(dest), function(err) { - if (err) return cb(err) - safe_write(cb) - }) - } else { - cb(err) - } - }) + var safe_write = function(cb) { + var tmpname = tempFile(dest) + fs.writeFile(tmpname, data, function(err) { + if (err) return cb(err) + renameTmp(tmpname, dest, cb) + }) + } + + safe_write(function(err) { + if (err && err.code === 'ENOENT') { + mkdirp(Path.dirname(dest), function(err) { + if (err) return cb(err) + safe_write(cb) + }) + } else { + cb(err) + } + }) } function write_stream(name) { - var stream = new mystreams.UploadTarballStream() - - var _ended = 0 - stream.on('end', function() { - _ended = 1 - }) - - fs.exists(name, function(exists) { - if (exists) return stream.emit('error', FSError('EEXISTS')) - - var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '') - , file = fs.createWriteStream(tmpname) - , opened = false - stream.pipe(file) - - stream.done = function() { - function onend() { - file.on('close', function() { - renameTmp(tmpname, name, function(err) { - if (err) { - stream.emit('error', err) - } else { - stream.emit('success') - } - }) - }) - file.destroySoon() - } - if (_ended) { - onend() - } else { - stream.on('end', onend) - } - } - stream.abort = function() { - if (opened) { - opened = false - file.on('close', function() { - fs.unlink(tmpname, function(){}) - }) - } - file.destroySoon() - } - file.on('open', function() { - opened = true - // re-emitting open because it's handled in storage.js - stream.emit('open') - }) - file.on('error', function(err) { - stream.emit('error', err) - }) - }) - return stream + var stream = MyStreams.UploadTarballStream() + + var _ended = 0 + stream.on('end', function() { + _ended = 1 + }) + + fs.exists(name, function(exists) { + if (exists) return stream.emit('error', FSError('EEXISTS')) + + var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '') + var file = fs.createWriteStream(tmpname) + var opened = false + stream.pipe(file) + + stream.done = function() { + function onend() { + file.on('close', function() { + renameTmp(tmpname, name, function(err) { + if (err) { + stream.emit('error', err) + } else { + stream.emit('success') + } + }) + }) + file.destroySoon() + } + if (_ended) { + onend() + } else { + stream.on('end', onend) + } + } + stream.abort = function() { + if (opened) { + opened = false + file.on('close', function() { + fs.unlink(tmpname, function(){}) + }) + } + file.destroySoon() + } + file.on('open', function() { + opened = true + // re-emitting open because it's handled in storage.js + stream.emit('open') + }) + file.on('error', function(err) { + stream.emit('error', err) + }) + }) + return stream } function read_stream(name, stream, callback) { - var rstream = fs.createReadStream(name) - rstream.on('error', function(err) { - stream.emit('error', err) - }) - rstream.on('open', function(fd) { - fs.fstat(fd, function(err, stats) { - if (err) return stream.emit('error', err) - stream.emit('content-length', stats.size) - stream.emit('open') - rstream.pipe(stream) - }) - }) - - var stream = new mystreams.ReadTarballStream() - stream.abort = function() { - rstream.close() - } - return stream + var rstream = fs.createReadStream(name) + rstream.on('error', function(err) { + stream.emit('error', err) + }) + rstream.on('open', function(fd) { + fs.fstat(fd, function(err, stats) { + if (err) return stream.emit('error', err) + stream.emit('content-length', stats.size) + stream.emit('open') + rstream.pipe(stream) + }) + }) + + var stream = MyStreams.ReadTarballStream() + stream.abort = function() { + rstream.close() + } + return stream } function create(name, contents, callback) { - fs.exists(name, function(exists) { - if (exists) return callback(FSError('EEXISTS')) - write(name, contents, callback) - }) + fs.exists(name, function(exists) { + if (exists) return callback( FSError('EEXISTS') ) + write(name, contents, callback) + }) } function update(name, contents, callback) { - fs.exists(name, function(exists) { - if (!exists) return callback(FSError('ENOENT')) - write(name, contents, callback) - }) + fs.exists(name, function(exists) { + if (!exists) return callback( FSError('ENOENT') ) + write(name, contents, callback) + }) } function read(name, callback) { - fs.readFile(name, callback) + fs.readFile(name, callback) } // open and flock with exponential backoff function open_flock(name, opmod, flmod, tries, backoff, cb) { - fs.open(name, opmod, function(err, fd) { - if (err) return cb(err, fd) - - fsExt.flock(fd, flmod, function(err) { - if (err) { - if (!tries) { - fs.close(fd, function() { - cb(err) - }) - } else { - fs.close(fd, function() { - setTimeout(function() { - open_flock(name, opmod, flmod, tries-1, backoff*2, cb) - }, backoff) - }) - } - } else { - cb(null, fd) - } - }) - }) + fs.open(name, opmod, function(err, fd) { + if (err) return cb(err, fd) + + fsExt.flock(fd, flmod, function(err) { + if (err) { + if (!tries) { + fs.close(fd, function() { + cb(err) + }) + } else { + fs.close(fd, function() { + setTimeout(function() { + open_flock(name, opmod, flmod, tries-1, backoff*2, cb) + }, backoff) + }) + } + } else { + cb(null, fd) + } + }) + }) } // this function neither unlocks file nor closes it // it'll have to be done manually later function lock_and_read(name, _callback) { - open_flock(name, 'r', 'exnb', 4, 10, function(err, fd) { - function callback(err) { - if (err && fd) { - fs.close(fd, function(err2) { - _callback(err) - }) - } else { - _callback.apply(null, arguments) - } - } - - if (err) return callback(err, fd) - - fs.fstat(fd, function(err, st) { - if (err) return callback(err, fd) - - var buffer = new Buffer(st.size) - if (st.size === 0) return onRead(null, 0, buffer) - fs.read(fd, buffer, 0, st.size, null, onRead) - - function onRead(err, bytesRead, buffer) { - if (err) return callback(err, fd) - if (bytesRead != st.size) return callback(new Error('st.size != bytesRead'), fd) - - callback(null, fd, buffer) - } - }) - }) + open_flock(name, 'r', 'exnb', 4, 10, function(err, fd) { + function callback(err) { + if (err && fd) { + fs.close(fd, function(err2) { + _callback(err) + }) + } else { + _callback.apply(null, arguments) + } + } + + if (err) return callback(err, fd) + + fs.fstat(fd, function(err, st) { + if (err) return callback(err, fd) + + var buffer = Buffer(st.size) + if (st.size === 0) return onRead(null, 0, buffer) + fs.read(fd, buffer, 0, st.size, null, onRead) + + function onRead(err, bytesRead, buffer) { + if (err) return callback(err, fd) + if (bytesRead != st.size) return callback(Error('st.size != bytesRead'), fd) + + callback(null, fd, buffer) + } + }) + }) } module.exports.read = read module.exports.read_json = function(name, cb) { - read(name, function(err, res) { - if (err) return cb(err) - - var args = [] - try { - args = [null, JSON.parse(res.toString('utf8'))] - } catch(err) { - args = [err] - } - cb.apply(null, args) - }) + read(name, function(err, res) { + if (err) return cb(err) + + var args = [] + try { + args = [ null, JSON.parse(res.toString('utf8')) ] + } catch(err) { + args = [ err ] + } + cb.apply(null, args) + }) } module.exports.lock_and_read = lock_and_read module.exports.lock_and_read_json = function(name, cb) { - lock_and_read(name, function(err, fd, res) { - if (err) return cb(err, fd) - - var args = [] - try { - args = [null, fd, JSON.parse(res.toString('utf8'))] - } catch(err) { - args = [err, fd] - } - cb.apply(null, args) - }) + lock_and_read(name, function(err, fd, res) { + if (err) return cb(err, fd) + + var args = [] + try { + args = [ null, fd, JSON.parse(res.toString('utf8')) ] + } catch(err) { + args = [ err, fd ] + } + cb.apply(null, args) + }) } module.exports.create = create module.exports.create_json = function(name, value, cb) { - create(name, JSON.stringify(value, null, '\t'), cb) + create(name, JSON.stringify(value, null, '\t'), cb) } module.exports.update = update module.exports.update_json = function(name, value, cb) { - update(name, JSON.stringify(value, null, '\t'), cb) + update(name, JSON.stringify(value, null, '\t'), cb) } module.exports.write = write module.exports.write_json = function(name, value, cb) { - write(name, JSON.stringify(value, null, '\t'), cb) + write(name, JSON.stringify(value, null, '\t'), cb) } module.exports.write_stream = write_stream diff --git a/lib/local-list.js b/lib/local-list.js index c4092644f732..4e81d1ae069a 100644 --- a/lib/local-list.js +++ b/lib/local-list.js @@ -1,38 +1,40 @@ var fs = require('fs') +module.exports = LocalList + function LocalList(path) { - var self = Object.create(LocalList.prototype) - self.path = path - try { - self.list = JSON.parse(fs.readFileSync(self.path, 'utf8')).list - } catch(_) { - self.list = [] - } - return self + var self = Object.create(LocalList.prototype) + self.path = path + try { + self.list = JSON.parse(fs.readFileSync(self.path, 'utf8')).list + } catch(_) { + self.list = [] + } + return self } -LocalList.prototype = { - add: function(name) { - if (this.list.indexOf(name) === -1) { - this.list.push(name) - this.sync() - } - }, - remove: function(name) { - var i = this.list.indexOf(name) - if (i !== -1) { - this.list.splice(i, 1) - } - - this.sync() - }, - get: function() { - return this.list - }, - sync: function() { - fs.writeFileSync(this.path, JSON.stringify({list: this.list})); //Uses sync to prevent ugly race condition - }, +LocalList.prototype.add = function(name) { + if (this.list.indexOf(name) === -1) { + this.list.push(name) + this.sync() + } } -module.exports = LocalList +LocalList.prototype.remove = function(name) { + var i = this.list.indexOf(name) + if (i !== -1) { + this.list.splice(i, 1) + } + + this.sync() +} + +LocalList.prototype.get = function() { + return this.list +} + +LocalList.prototype.sync = function() { + // Uses sync to prevent ugly race condition + fs.writeFileSync(this.path, JSON.stringify({ list: this.list })) +} diff --git a/lib/local-storage.js b/lib/local-storage.js index 18c85b050096..7002c0f38405 100644 --- a/lib/local-storage.js +++ b/lib/local-storage.js @@ -1,481 +1,483 @@ -var fs = require('fs') - , Path = require('path') - , crypto = require('crypto') - , assert = require('assert') - , fs_storage = require('./local-fs') - , Error = require('http-errors') - , utils = require('./utils') - , mystreams = require('./streams') - , Logger = require('./logger') - , info_file = 'package.json' - , search = require('./search'); +var assert = require('assert') +var Crypto = require('crypto') +var fs = require('fs') +var Error = require('http-errors') +var Path = require('path') +var fs_storage = require('./local-fs') +var Logger = require('./logger') +var Search = require('./search') +var MyStreams = require('./streams') +var Utils = require('./utils') +var info_file = 'package.json' // // Implements Storage interface // (same for storage.js, local-storage.js, up-storage.js) // function Storage(config) { - if (!(this instanceof Storage)) return new Storage(config) - this.config = config - this.logger = Logger.logger.child({sub: 'fs'}) - return this + var self = Object.create(Storage.prototype) + self.config = config + self.logger = Logger.logger.child({ sub: 'fs' }) + return self } // returns the minimal package file function get_boilerplate(name) { - return { - // standard things - name: name, - versions: {}, - 'dist-tags': {}, - - // our own object - '_distfiles': {}, - '_attachments': {}, - '_uplinks': {}, - } + return { + // standard things + name: name, + versions: {}, + 'dist-tags': {}, + + // our own object + '_distfiles': {}, + '_attachments': {}, + '_uplinks': {}, + } } Storage.prototype._internal_error = function(err, file, message) { - this.logger.error( {err: err, file: file} - , message + ' @{file}: @{!err.message}' - ) - return Error[500]() + this.logger.error( { err: err, file: file } + , message + ' @{file}: @{!err.message}' ) + return Error[500]() } Storage.prototype.add_package = function(name, info, callback) { - var self = this - var storage = this.storage(name) - if (!storage) return callback(Error[404]('this package cannot be added')) - - storage.create_json(info_file, get_boilerplate(name), function(err) { - if (err && err.code === 'EEXISTS') { - return callback(Error[409]('this package is already present')) - } - - var latest = info['dist-tags'].latest - if (latest && info.versions[latest]) { - search.add(info.versions[latest]) - } - callback() - }) + var self = this + var storage = this.storage(name) + if (!storage) return callback( Error[404]('this package cannot be added') ) + + storage.create_json(info_file, get_boilerplate(name), function(err) { + if (err && err.code === 'EEXISTS') { + return callback( Error[409]('this package is already present') ) + } + + var latest = info['dist-tags'].latest + if (latest && info.versions[latest]) { + Search.add(info.versions[latest]) + } + callback() + }) } Storage.prototype.remove_package = function(name, callback) { - var self = this - self.logger.info({name: name}, 'unpublishing @{name} (all)') - - var storage = self.storage(name) - if (!storage) return callback(Error[404]('no such package available')) - - storage.read_json(info_file, function(err, data) { - if (err) { - if (err.code === 'ENOENT') { - return callback(Error[404]('no such package available')) - } else { - return callback(err) - } - } - self._normalize_package(data) - - storage.unlink(info_file, function(err) { - if (err) return callback(err) - - var files = Object.keys(data._attachments) - - function unlinkNext(cb) { - if (files.length === 0) return cb() - - var file = files.shift() - storage.unlink(file, function() { - unlinkNext(cb) - }) - } - - unlinkNext(function() { - // try to unlink the directory, but ignore errors because it can fail - storage.rmdir('.', function(err) { - callback(err) - }); - }); - }); - }); - - search.remove(name); - this.config.localList.remove(name); + var self = this + self.logger.info( { name: name } + , 'unpublishing @{name} (all)') + + var storage = self.storage(name) + if (!storage) return callback( Error[404]('no such package available') ) + + storage.read_json(info_file, function(err, data) { + if (err) { + if (err.code === 'ENOENT') { + return callback( Error[404]('no such package available') ) + } else { + return callback(err) + } + } + self._normalize_package(data) + + storage.unlink(info_file, function(err) { + if (err) return callback(err) + + var files = Object.keys(data._attachments) + + function unlinkNext(cb) { + if (files.length === 0) return cb() + + var file = files.shift() + storage.unlink(file, function() { + unlinkNext(cb) + }) + } + + unlinkNext(function() { + // try to unlink the directory, but ignore errors because it can fail + storage.rmdir('.', function(err) { + callback(err) + }) + }) + }) + }) + + Search.remove(name) + this.config.localList.remove(name) } Storage.prototype._read_create_package = function(name, callback) { - var self = this - var storage = self.storage(name) - if (!storage) { - var data = get_boilerplate(name) - self._normalize_package(data) - return callback(null, data) - } - storage.read_json(info_file, function(err, data) { - // TODO: race condition - if (err) { - if (err.code === 'ENOENT') { - // if package doesn't exist, we create it here - data = get_boilerplate(name) - } else { - return callback(self._internal_error(err, info_file, 'error reading')) - } - } - self._normalize_package(data) - callback(null, data) - }) + var self = this + var storage = self.storage(name) + if (!storage) { + var data = get_boilerplate(name) + self._normalize_package(data) + return callback(null, data) + } + storage.read_json(info_file, function(err, data) { + // TODO: race condition + if (err) { + if (err.code === 'ENOENT') { + // if package doesn't exist, we create it here + data = get_boilerplate(name) + } else { + return callback(self._internal_error(err, info_file, 'error reading')) + } + } + self._normalize_package(data) + callback(null, data) + }) } // synchronize remote package info with the local one // TODO: readfile called twice Storage.prototype.update_versions = function(name, newdata, callback) { - var self = this - self._read_create_package(name, function(err, data) { - if (err) return callback(err) - - var change = false - for (var ver in newdata.versions) { - if (data.versions[ver] == null) { - var verdata = newdata.versions[ver] - - // we don't keep readmes for package versions, - // only one readme per package - delete verdata.readme - - change = true - data.versions[ver] = verdata - - if (verdata.dist && verdata.dist.tarball) { - var url = utils.parse_tarball_url( - /*verdata.dist.__sinopia_orig_tarball ||*/ verdata.dist.tarball - ) - - // we do NOT overwrite any existing records - if (url != null && data._distfiles[url.filename] == null) { - data._distfiles[url.filename] = { - url: /*verdata.dist.__sinopia_orig_tarball ||*/ verdata.dist.tarball, - sha: verdata.dist.shasum, - } - } - } - } - } - for (var tag in newdata['dist-tags']) { - if (!Array.isArray(data['dist-tags'][tag]) || data['dist-tags'][tag].length != newdata['dist-tags'][tag].length) { - // backward compat - var need_change = true - } else { - for (var i=0; i startkey && utils.validate_name(file)) { - list.push({ - time: stats.mtime, - name: file - }) - } - if (++i !== filesL) { - return false - } - return callback(null, list) - }) - }) - }) + var self = this + var i = 0 + var list = [] + + var storage = self.storage('') + if (!storage) return callback(null, []) + + fs.readdir(storage.path, function(err, files) { + if (err) return callback(null, []) + + var filesL = files.length + + files.forEach(function(file) { + fs.stat(storage.path, function(err, stats) { + if (err) return callback(err) + if (stats.mtime > startkey && Utils.validate_name(file)) { + list.push({ + time: stats.mtime, + name: file + }) + } + if (++i !== filesL) { + return false + } + return callback(null, list) + }) + }) + }) } // @@ -495,96 +497,99 @@ Storage.prototype.get_recent_packages = function(startkey, callback) { // 6. callback(err?) // Storage.prototype.update_package = function(name, updateFn, _callback) { - var self = this - var storage = self.storage(name) - if (!storage) return _callback(Error[404]('no such package available')) - storage.lock_and_read_json(info_file, function(err, fd, json) { - function callback() { - var _args = arguments - if (fd) { - fs.close(fd, function(err) { - if (err) return _callback(err) - _callback.apply(null, _args) - }) - } else { - _callback.apply(null, _args) - } - } - - if (err) { - if (err.code === 'EAGAIN') { - return callback(Error[503]('resource temporarily unavailable')) - } else if (err.code === 'ENOENT') { - return callback(Error[404]('no such package available')) - } else { - return callback(err) - } - } - - self._normalize_package(json) - updateFn(json, function(err) { - if (err) return callback(err) - - self._write_package(name, json, callback) - }) - }) + var self = this + var storage = self.storage(name) + if (!storage) return _callback( Error[404]('no such package available') ) + storage.lock_and_read_json(info_file, function(err, fd, json) { + function callback() { + var _args = arguments + if (fd) { + fs.close(fd, function(err) { + if (err) return _callback(err) + _callback.apply(null, _args) + }) + } else { + _callback.apply(null, _args) + } + } + + if (err) { + if (err.code === 'EAGAIN') { + return callback( Error[503]('resource temporarily unavailable') ) + } else if (err.code === 'ENOENT') { + return callback( Error[404]('no such package available') ) + } else { + return callback(err) + } + } + + self._normalize_package(json) + updateFn(json, function(err) { + if (err) return callback(err) + + self._write_package(name, json, callback) + }) + }) } Storage.prototype._normalize_package = function(pkg) { - ;['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks'].forEach(function(key) { - if (!utils.is_object(pkg[key])) pkg[key] = {} - }) - if (typeof(pkg._rev) !== 'string') pkg._rev = '0-0000000000000000' + ;['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks'].forEach(function(key) { + if (!Utils.is_object(pkg[key])) pkg[key] = {} + }) + if (typeof(pkg._rev) !== 'string') pkg._rev = '0-0000000000000000' } Storage.prototype._write_package = function(name, json, callback) { - // calculate revision a la couchdb - if (typeof(json._rev) !== 'string') json._rev = '0-0000000000000000' - var rev = json._rev.split('-') - json._rev = ((+rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(8).toString('hex') + // calculate revision a la couchdb + if (typeof(json._rev) !== 'string') json._rev = '0-0000000000000000' + var rev = json._rev.split('-') + json._rev = ((+rev[0] || 0) + 1) + '-' + Crypto.pseudoRandomBytes(8).toString('hex') - var storage = this.storage(name) - if (!storage) return callback() - storage.write_json(info_file, json, callback) + var storage = this.storage(name) + if (!storage) return callback() + storage.write_json(info_file, json, callback) } Storage.prototype.storage = function(package) { - var path = this.config.get_package_setting(package, 'storage') - if (path == null) path = this.config.storage - if (path == null || path === false) { - this.logger.debug({name: package}, 'this package has no storage defined: @{name}') - return null - } - return new Path_Wrapper( - Path.join( - Path.resolve(Path.dirname(this.config.self_path), path), - package - ) - ) + var path = this.config.get_package_setting(package, 'storage') + if (path == null) path = this.config.storage + if (path == null || path === false) { + this.logger.debug( { name: package } + , 'this package has no storage defined: @{name}' ) + return null + } + return Path_Wrapper( + Path.join( + Path.resolve(Path.dirname(this.config.self_path), path), + package + ) + ) } var Path_Wrapper = (function() { - // a wrapper adding paths to fs_storage methods - function Wrapper(path) { - this.path = path - } - - for (var i in fs_storage) { - if (fs_storage.hasOwnProperty(i)) { - Wrapper.prototype[i] = wrapper(i) - } - } - - function wrapper(method) { - return function(/*...*/) { - var args = Array.prototype.slice.apply(arguments) - args[0] = Path.join(this.path, args[0] || '') - return fs_storage[method].apply(null, args) - } - } - - return Wrapper + // a wrapper adding paths to fs_storage methods + function Wrapper(path) { + var self = Object.create(Wrapper.prototype) + self.path = path + return self + } + + for (var i in fs_storage) { + if (fs_storage.hasOwnProperty(i)) { + Wrapper.prototype[i] = wrapper(i) + } + } + + function wrapper(method) { + return function(/*...*/) { + var args = Array.prototype.slice.apply(arguments) + args[0] = Path.join(this.path, args[0] || '') + return fs_storage[method].apply(null, args) + } + } + + return Wrapper })() module.exports = Storage diff --git a/lib/logger.js b/lib/logger.js index 753085ae8502..50eef1ba76e8 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,77 +1,77 @@ var Logger = require('bunyan') - , Error = require('http-errors') - , Stream = require('stream') - , utils = require('./utils') +var Error = require('http-errors') +var Stream = require('stream') +var Utils = require('./utils') function getlvl(x) { - switch(true) { - case x < 15: return 'trace' - case x < 25: return 'debug' - case x < 35: return 'info' - case x == 35: return 'http' - case x < 45: return 'warn' - case x < 55: return 'error' - default: return 'fatal' - } + switch(true) { + case x < 15 : return 'trace' + case x < 25 : return 'debug' + case x < 35 : return 'info' + case x == 35 : return 'http' + case x < 45 : return 'warn' + case x < 55 : return 'error' + default : return 'fatal' + } } module.exports.setup = function(logs) { - var streams = [] - if (logs == null) logs = [{ type: 'stdout', format: 'pretty', level: 'http' }] - - logs.forEach(function(target) { - var stream = new Stream() - stream.writable = true - - if (target.type === 'stdout' || target.type === 'stderr') { - // destination stream - var dest = target.type === 'stdout' ? process.stdout : process.stderr - - if (target.format === 'pretty') { - // making fake stream for prettypritting - stream.write = function(obj) { - dest.write(print(obj.level, obj.msg, obj, dest.isTTY) + '\n') - } - } else { - stream.write = function(obj) { - dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n') - } - } - } else if (target.type === 'file') { - var dest = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'}) - dest.on('error', function (err) { - Logger.emit('error', err) - }) - stream.write = function(obj) { - if (target.format === 'pretty') { - dest.write(print(obj.level, obj.msg, obj, false) + '\n') - } else { - dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n') - } - } - } else { - throw Error('wrong target type for a log') - } - - if (target.level === 'http') target.level = 35 - streams.push({ - type: 'raw', - level: target.level || 35, - stream: stream, - }) - }) - - var logger = new Logger({ - name: 'sinopia', - streams: streams, - serializers: { - err: Logger.stdSerializers.err, - req: Logger.stdSerializers.req, - res: Logger.stdSerializers.res, - }, - }) - - module.exports.logger = logger + var streams = [] + if (logs == null) logs = [{ type: 'stdout', format: 'pretty', level: 'http' }] + + logs.forEach(function(target) { + var stream = new Stream() + stream.writable = true + + if (target.type === 'stdout' || target.type === 'stderr') { + // destination stream + var dest = target.type === 'stdout' ? process.stdout : process.stderr + + if (target.format === 'pretty') { + // making fake stream for prettypritting + stream.write = function(obj) { + dest.write(print(obj.level, obj.msg, obj, dest.isTTY) + '\n') + } + } else { + stream.write = function(obj) { + dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n') + } + } + } else if (target.type === 'file') { + var dest = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'}) + dest.on('error', function (err) { + Logger.emit('error', err) + }) + stream.write = function(obj) { + if (target.format === 'pretty') { + dest.write(print(obj.level, obj.msg, obj, false) + '\n') + } else { + dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n') + } + } + } else { + throw Error('wrong target type for a log') + } + + if (target.level === 'http') target.level = 35 + streams.push({ + type: 'raw', + level: target.level || 35, + stream: stream, + }) + }) + + var logger = new Logger({ + name: 'sinopia', + streams: streams, + serializers: { + err: Logger.stdSerializers.err, + req: Logger.stdSerializers.req, + res: Logger.stdSerializers.res, + }, + }) + + module.exports.logger = logger } // adopted from socket.io @@ -80,75 +80,74 @@ module.exports.setup = function(logs) { // level to color var levels = { - fatal: 31, - error: 31, - warn: 33, - http: 35, - info: 36, - debug: 90, - trace: 90, + fatal : 31, + error : 31, + warn : 33, + http : 35, + info : 36, + debug : 90, + trace : 90, } var max = 0 for (var l in levels) { - max = Math.max(max, l.length) + max = Math.max(max, l.length) } function pad(str) { - if (str.length < max) return str + new Array(max - str.length + 1).join(' ') - return str + if (str.length < max) return str + Array(max - str.length + 1).join(' ') + return str } var subsystems = [{ - in: '\033[32m<--\033[39m', - out: '\033[33m-->\033[39m', - fs: '\033[90m-=-\033[39m', - default: '\033[34m---\033[39m', + in : '\033[32m<--\033[39m', + out : '\033[33m-->\033[39m', + fs : '\033[90m-=-\033[39m', + default : '\033[34m---\033[39m', }, { - in: '<--', - out: '-->', - fs: '-=-', - default: '---', + in : '<--', + out : '-->', + fs : '-=-', + default : '---', }] function print(type, msg, obj, colors) { - if (typeof type === 'number') type = getlvl(type) - var finalmsg = msg.replace(/@{(!?[$A-Za-z_][$0-9A-Za-z\._]*)}/g, function(_, name) { - var str = obj, is_error - if (name[0] === '!') { - name = name.substr(1) - is_error = true - } - - var _ref = name.split('.') - for (var _i = 0; _i < _ref.length; _i++) { - var id = _ref[_i] - if (utils.is_object(str) || Array.isArray(str)) { - str = str[id] - } else { - str = undefined - } - } - - if (typeof(str) === 'string') { - if (!colors || ~str.indexOf('\n')) { - return str - } else if (is_error) { - return '\033[31m' + str + '\033[39m' - } else { - return '\033[32m' + str + '\033[39m' - } - } else { - return require('util').inspect(str, void 0, void 0, colors) - } - }) - var sub = subsystems[+!colors][obj.sub] || subsystems[+!colors].default - // ^^--- black magic... kidding, just "colors ? 0 : 1" - - if (colors) { - return ' \033[' + levels[type] + 'm' + (pad(type)) + '\033[39m ' + sub + ' ' + finalmsg - } else { - return ' ' + (pad(type)) + ' ' + sub + ' ' + finalmsg - } + if (typeof type === 'number') type = getlvl(type) + var finalmsg = msg.replace(/@{(!?[$A-Za-z_][$0-9A-Za-z\._]*)}/g, function(_, name) { + var str = obj, is_error + if (name[0] === '!') { + name = name.substr(1) + is_error = true + } + + var _ref = name.split('.') + for (var _i = 0; _i < _ref.length; _i++) { + var id = _ref[_i] + if (Utils.is_object(str) || Array.isArray(str)) { + str = str[id] + } else { + str = undefined + } + } + + if (typeof(str) === 'string') { + if (!colors || ~str.indexOf('\n')) { + return str + } else if (is_error) { + return '\033[31m' + str + '\033[39m' + } else { + return '\033[32m' + str + '\033[39m' + } + } else { + return require('util').inspect(str, null, null, colors) + } + }) + + var sub = subsystems[colors ? 0 : 1][obj.sub] || subsystems[+!colors].default + if (colors) { + return ' \033[' + levels[type] + 'm' + (pad(type)) + '\033[39m ' + sub + ' ' + finalmsg + } else { + return ' ' + (pad(type)) + ' ' + sub + ' ' + finalmsg + } } diff --git a/lib/middleware.js b/lib/middleware.js index 2e47657e9eca..2c1881593628 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -1,151 +1,150 @@ var crypto = require('crypto') - , Error = require('http-errors') - , utils = require('./utils') - , Logger = require('./logger') +var Error = require('http-errors') +var utils = require('./utils') +var Logger = require('./logger') module.exports.validate_name = function validate_name(req, res, next, value, name) { - if (value.charAt(0) === '-') { - // special case in couchdb usually - next('route') - } else if (utils.validate_name(value)) { - next() - } else { - next(Error[403]('invalid ' + name)) - } + if (value.charAt(0) === '-') { + // special case in couchdb usually + next('route') + } else if (utils.validate_name(value)) { + next() + } else { + next( Error[403]('invalid ' + name) ) + } } module.exports.media = function media(expect) { - return function(req, res, next) { - if (req.headers['content-type'] !== expect) { - next(Error[415]('wrong content-type, expect: ' + expect - + ', got: '+req.headers['content-type'])) - } else { - next() - } - } + return function(req, res, next) { + if (req.headers['content-type'] !== expect) { + next( Error[415]('wrong content-type, expect: ' + expect + + ', got: '+req.headers['content-type']) ) + } else { + next() + } + } } module.exports.expect_json = function expect_json(req, res, next) { - if (!utils.is_object(req.body)) { - return next({ - status: 400, - message: 'can\'t parse incoming json', - }) - } - next() + if (!utils.is_object(req.body)) { + return next( Error[400]("can't parse incoming json") ) + } + next() } module.exports.anti_loop = function(config) { - return function(req, res, next) { - if (req.headers.via != null) { - var arr = req.headers.via.split(',') - for (var i=0; i= 200 && res.statusCode < 300)) { - res.header('ETag', '"' + md5sum(body) + '"') - } - } else { - // send(null), send(204), etc. - } - } catch(err) { - // if sinopia sends headers first, and then calls res.send() - // as an error handler, we can't report error properly, - // and should just close socket - if (err.message.match(/set headers after they are sent/)) { - if (res.socket != null) res.socket.destroy() - return - } else { - throw err - } - } - - res.send = _send - res.send(body) - } - - var bytesout = 0 - , _write = res.write - res.write = function(buf) { - bytesout += buf.length - _write.apply(res, arguments) - } - - function log() { - var message = '@{status}, user: @{user}, req: \'@{request.method} @{request.url}\'' - if (res._sinopia_error) { - message += ', error: @{!error}' - } else { - message += ', bytes: @{bytes.in}/@{bytes.out}' - } - req.log.warn({ - request: {method: req.method, url: req.url}, - level: 35, // http - user: req.remote_user.name, - status: res.statusCode, - error: res._sinopia_error, - bytes: { - in: bytesin, - out: bytesout, - } - }, message) - } - - req.on('close', function() { - log(true) - }) - - var _end = res.end - res.end = function(buf) { - if (buf) bytesout += buf.length - _end.apply(res, arguments) - log() - } - next() + // logger + req.log = Logger.logger.child({ sub: 'in' }) + + var _auth = req.headers.authorization + if (_auth) req.headers.authorization = '' + req.log.info( { req: req, ip: req.ip } + , '@{ip} requested \'@{req.method} @{req.url}\'' ) + if (_auth) req.headers.authorization = _auth + + var bytesin = 0 + req.on('data', function(chunk) { + bytesin += chunk.length + }) + + var _send = res.send + res.send = function(body) { + try { + if (typeof(body) === 'string' || typeof(body) === 'object') { + if (!res.getHeader('Content-type')) { + res.header('Content-type', 'application/json') + } + + if (typeof(body) === 'object' && body != null) { + if (typeof(body.error) === 'string') { + res._sinopia_error = body.error + } + body = JSON.stringify(body, undefined, '\t') + '\n' + } + + // don't send etags with errors + if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) { + res.header('ETag', '"' + md5sum(body) + '"') + } + } else { + // send(null), send(204), etc. + } + } catch(err) { + // if sinopia sends headers first, and then calls res.send() + // as an error handler, we can't report error properly, + // and should just close socket + if (err.message.match(/set headers after they are sent/)) { + if (res.socket != null) res.socket.destroy() + return + } else { + throw err + } + } + + res.send = _send + res.send(body) + } + + var bytesout = 0 + var _write = res.write + res.write = function(buf) { + bytesout += buf.length + _write.apply(res, arguments) + } + + function log() { + var message = "@{status}, user: @{user}, req: '@{request.method} @{request.url}'" + if (res._sinopia_error) { + message += ', error: @{!error}' + } else { + message += ', bytes: @{bytes.in}/@{bytes.out}' + } + req.log.warn({ + request : { method: req.method, url: req.url }, + level : 35, // http + user : req.remote_user.name, + status : res.statusCode, + error : res._sinopia_error, + bytes : { + in : bytesin, + out : bytesout, + } + }, message) + } + + req.on('close', function() { + log(true) + }) + + var _end = res.end + res.end = function(buf) { + if (buf) bytesout += buf.length + _end.apply(res, arguments) + log() + } + next() } diff --git a/lib/search.js b/lib/search.js index 3363affca764..27d46b1bdda3 100644 --- a/lib/search.js +++ b/lib/search.js @@ -1,45 +1,48 @@ var lunr = require('lunr') -var Search = function() { - this.index = lunr(function () { - this.field('name', {boost: 10}); - this.field('description', {boost: 4}); - this.field('author', {boost: 6}); - this.field('readme'); - }); -}; - -Search.prototype = { - query: function(q) { - return this.index.search(q); - }, - add: function(package) { - this.index.add({ - id: package.name, - name: package.name, - description: package.description, - author: package._npmUser ? package._npmUser.name : '???' - }); - }, - remove: function(name) { - this.index.remove({ - id: name - }); - }, - reindex: function() { - var self = this; - this.storage.get_local(function(err, packages) { - var i = packages.length; - - while(i--) { - self.add(packages[i]); - } - }); - }, - configureStorage: function(storage) { - this.storage = storage; - this.reindex(); - } -}; - -module.exports = new Search(); +function Search() { + var self = Object.create(Search.prototype) + self.index = lunr(function() { + this.field('name' , { boost: 10 }) + this.field('description' , { boost: 4 }) + this.field('author' , { boost: 6 }) + this.field('readme') + }) + return self +} + +Search.prototype.query = function(q) { + return this.index.search(q) +} + +Search.prototype.add = function(package) { + this.index.add({ + id: package.name, + name: package.name, + description: package.description, + author: package._npmUser ? package._npmUser.name : '???', + }) +}, + +Search.prototype.remove = function(name) { + this.index.remove({ id: name }) +} + +Search.prototype.reindex = function() { + var self = this + this.storage.get_local(function(err, packages) { + if (err) throw err // that function shouldn't produce any + var i = packages.length + while (i--) { + self.add(packages[i]) + } + }) +} + +Search.prototype.configureStorage = function(storage) { + this.storage = storage + this.reindex() +} + +module.exports = Search() + diff --git a/lib/status-cats.js b/lib/status-cats.js index 2cb66c6a8d66..5c0016a86242 100644 --- a/lib/status-cats.js +++ b/lib/status-cats.js @@ -2,73 +2,73 @@ // see https://secure.flickr.com/photos/girliemac/sets/72157628409467125 var images = { - 100: 'aVvDhR', // '6512768893', // 100 - Continue - 101: 'aXXExP', // '6540479029', // 101 - Switching Protocols - 200: 'aVuVsF', // '6512628175', // 200 - OK - 201: 'aXWm1Z', // '6540221577', // 201 - Created - 202: 'aXXEyF', // '6540479079', // 202 - Accepted - 204: 'aYyJ7B', // '6547319943', // 204 - No Content - 206: 'aVEnUP', // '6514473163', // 206 - Partial Content - 207: 'aVEnRD', // '6514472979', // 207 - Multi-Status - 300: 'aW7mac', // '6519540181', // 300 - Multiple Choices - 301: 'aW7mb4', // '6519540231', // 301 - Moved Permanently - 302: 'aV6jKp', // '6508023829', // 302 - Found - 303: 'aVxtaK', // '6513125065', // 303 - See Other - 304: 'aXY3dH', // '6540551929', // 304 - Not Modified - 305: 'aXX5LK', // '6540365403', // 305 - Use Proxy - 307: 'aVwQnk', // '6513001269', // 307 - Temporary Redirect - 400: 'aXYDeT', // '6540669737', // 400 - Bad Request - 401: 'aV6jwe', // '6508023065', // 401 - Unauthorized - 402: 'aVwQoe', // '6513001321', // 402 - Payment Required - 403: 'aV6jFK', // '6508023617', // 403 - Forbidden - 404: 'aV6juR', // '6508022985', // 404 - Not Found - 405: 'aV6jE8', // '6508023523', // 405 - Method Not Allowed - 406: 'aV6jxa', // '6508023119', // 406 - Not Acceptable - 408: 'aV6jyc', // '6508023179', // 408 - Request Timeout - 409: 'aV6jzz', // '6508023259', // 409 - Conflict - 410: 'aVES2H', // '6514567755', // 410 - Gone - 411: 'aXYVpT', // '6540724141', // 411 - Length Required - 413: 'aV6jHZ', // '6508023747', // 413 - Request Entity Too Large - 414: 'aV6jBa', // '6508023351', // 414 - Request-URI Too Long - 416: 'aVxQvr', // '6513196851', // 416 - Requested Range Not Satisfiable - 417: 'aV6jGP', // '6508023679', // 417 - Expectation Failed - 418: 'aV6J7c', // '6508102407', // 418 - I'm a teapot - 422: 'aVEnTt', // '6514473085', // 422 - Unprocessable Entity - 423: 'aVEyVZ', // '6514510235', // 423 - Locked - 424: 'aVEWZ6', // '6514584423', // 424 - Failed Dependency - 425: 'aXYdzH', // '6540586787', // 425 - Unordered Collection - 426: 'aVdo4M', // '6509400771', // 426 - Upgrade Required - 429: 'aVdo8F', // '6509400997', // 429 - Too Many Requests - 431: 'aVdo3n', // '6509400689', // 431 - Request Header Fields Too Large - 444: 'aVdo1P', // '6509400599', // 444 - No Response - 450: 'aVxtbK', // '6513125123', // 450 - Blocked by Windows Parental Controls - 451: 'eTiGQd', // '9113233540', // 451 - Unavailable for Legal Reasons - 500: 'aVdo6e', // '6509400855', // 500 - Internal Server Error - 502: 'aV6jCv', // '6508023429', // 502 - Bad Gateway - 503: 'aXYvop', // '6540643319', // 503 - Service Unavailable - 506: 'aXYvnH', // '6540643279', // 506 - Variant Also Negotiates - 507: 'aVdnZa', // '6509400503', // 507 - Insufficient Storage - 508: 'aVdnYa', // '6509400445', // 508 - Loop Detected - 509: 'aXXg1V', // '6540399865', // 509 - Bandwidth Limit Exceeded - 599: 'aVdo7v', // '6509400929', // 599 - Network connect timeout error + 100: 'aVvDhR', // '6512768893', // 100 - Continue + 101: 'aXXExP', // '6540479029', // 101 - Switching Protocols + 200: 'aVuVsF', // '6512628175', // 200 - OK + 201: 'aXWm1Z', // '6540221577', // 201 - Created + 202: 'aXXEyF', // '6540479079', // 202 - Accepted + 204: 'aYyJ7B', // '6547319943', // 204 - No Content + 206: 'aVEnUP', // '6514473163', // 206 - Partial Content + 207: 'aVEnRD', // '6514472979', // 207 - Multi-Status + 300: 'aW7mac', // '6519540181', // 300 - Multiple Choices + 301: 'aW7mb4', // '6519540231', // 301 - Moved Permanently + 302: 'aV6jKp', // '6508023829', // 302 - Found + 303: 'aVxtaK', // '6513125065', // 303 - See Other + 304: 'aXY3dH', // '6540551929', // 304 - Not Modified + 305: 'aXX5LK', // '6540365403', // 305 - Use Proxy + 307: 'aVwQnk', // '6513001269', // 307 - Temporary Redirect + 400: 'aXYDeT', // '6540669737', // 400 - Bad Request + 401: 'aV6jwe', // '6508023065', // 401 - Unauthorized + 402: 'aVwQoe', // '6513001321', // 402 - Payment Required + 403: 'aV6jFK', // '6508023617', // 403 - Forbidden + 404: 'aV6juR', // '6508022985', // 404 - Not Found + 405: 'aV6jE8', // '6508023523', // 405 - Method Not Allowed + 406: 'aV6jxa', // '6508023119', // 406 - Not Acceptable + 408: 'aV6jyc', // '6508023179', // 408 - Request Timeout + 409: 'aV6jzz', // '6508023259', // 409 - Conflict + 410: 'aVES2H', // '6514567755', // 410 - Gone + 411: 'aXYVpT', // '6540724141', // 411 - Length Required + 413: 'aV6jHZ', // '6508023747', // 413 - Request Entity Too Large + 414: 'aV6jBa', // '6508023351', // 414 - Request-URI Too Long + 416: 'aVxQvr', // '6513196851', // 416 - Requested Range Not Satisfiable + 417: 'aV6jGP', // '6508023679', // 417 - Expectation Failed + 418: 'aV6J7c', // '6508102407', // 418 - I'm a teapot + 422: 'aVEnTt', // '6514473085', // 422 - Unprocessable Entity + 423: 'aVEyVZ', // '6514510235', // 423 - Locked + 424: 'aVEWZ6', // '6514584423', // 424 - Failed Dependency + 425: 'aXYdzH', // '6540586787', // 425 - Unordered Collection + 426: 'aVdo4M', // '6509400771', // 426 - Upgrade Required + 429: 'aVdo8F', // '6509400997', // 429 - Too Many Requests + 431: 'aVdo3n', // '6509400689', // 431 - Request Header Fields Too Large + 444: 'aVdo1P', // '6509400599', // 444 - No Response + 450: 'aVxtbK', // '6513125123', // 450 - Blocked by Windows Parental Controls + 451: 'eTiGQd', // '9113233540', // 451 - Unavailable for Legal Reasons + 500: 'aVdo6e', // '6509400855', // 500 - Internal Server Error + 502: 'aV6jCv', // '6508023429', // 502 - Bad Gateway + 503: 'aXYvop', // '6540643319', // 503 - Service Unavailable + 506: 'aXYvnH', // '6540643279', // 506 - Variant Also Negotiates + 507: 'aVdnZa', // '6509400503', // 507 - Insufficient Storage + 508: 'aVdnYa', // '6509400445', // 508 - Loop Detected + 509: 'aXXg1V', // '6540399865', // 509 - Bandwidth Limit Exceeded + 599: 'aVdo7v', // '6509400929', // 599 - Network connect timeout error } module.exports.get_image = function(status) { - if (status in images) { - return 'http://flic.kr/p/'+images[status] - //return 'https://secure.flickr.com/photos/girliemac/'+images[status]+'/in/set-72157628409467125/lightbox/' - } + if (status in images) { + return 'http://flic.kr/p/' + images[status] + //return 'https://secure.flickr.com/photos/girliemac/'+images[status]+'/in/set-72157628409467125/lightbox/' + } } module.exports.middleware = function(req, res, next) { - var _writeHead = res.writeHead - res.writeHead = function(status) { - if (status in images) { - res.setHeader('X-Status-Cat', module.exports.get_image(status)) - } - _writeHead.apply(res, arguments) - } + var _writeHead = res.writeHead + res.writeHead = function(status) { + if (status in images) { + res.setHeader('X-Status-Cat', module.exports.get_image(status)) + } + _writeHead.apply(res, arguments) + } - next() + next() } diff --git a/lib/storage.js b/lib/storage.js index 04f7667ef34d..a2d5279ff04f 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -1,32 +1,33 @@ -var async = require('async') - , assert = require('assert') - , Error = require('http-errors') - , Local = require('./local-storage') - , Proxy = require('./up-storage') - , mystreams = require('./streams') - , utils = require('./utils') - , Logger = require('./logger') +var assert = require('assert') +var async = require('async') +var Error = require('http-errors') +var Local = require('./local-storage') +var Logger = require('./logger') +var MyStreams = require('./streams') +var Proxy = require('./up-storage') +var Utils = require('./utils') + +module.exports = Storage // // Implements Storage interface // (same for storage.js, local-storage.js, up-storage.js) // function Storage(config) { - if (!(this instanceof Storage)) return new Storage(config) - - this.config = config - - // we support a number of uplinks, but only one local storage - // Proxy and Local classes should have similar API interfaces - this.uplinks = {} - for (var p in config.uplinks) { - this.uplinks[p] = new Proxy(config.uplinks[p], config) - this.uplinks[p].upname = p - } - this.local = new Local(config) - this.logger = Logger.logger.child() - - return this + var self = Object.create(Storage.prototype) + self.config = config + + // we support a number of uplinks, but only one local storage + // Proxy and Local classes should have similar API interfaces + self.uplinks = {} + for (var p in config.uplinks) { + self.uplinks[p] = Proxy(config.uplinks[p], config) + self.uplinks[p].upname = p + } + self.local = Local(config) + self.logger = Logger.logger.child() + + return self } // @@ -38,65 +39,61 @@ function Storage(config) { // Used storages: local (write) && uplinks // Storage.prototype.add_package = function(name, metadata, callback) { - var self = this - - // NOTE: - // - when we checking package for existance, we ask ALL uplinks - // - when we publishing package, we only publish it to some of them - // so all requests are necessary - - check_package_local(function(err) { - if (err) return callback(err) - - check_package_remote(function(err) { - if (err) return callback(err) - - publish_package(function(err) { - if (err) return callback(err) - callback() - }) - }) - }) - - function check_package_local(cb) { - self.local.get_package(name, {}, function(err, results) { - if (err && err.status !== 404) return cb(err) - - if (results) { - return cb(Error[409]('this package is already present')) - } - - cb() - }) - } - - function check_package_remote(cb) { - self._sync_package_with_uplinks(name, null, {}, function(err, results, err_results) { - // something weird - if (err && err.status !== 404) return cb(err) - - // checking package - if (results) { - return cb(Error[409]('this package is already present')) - } - - for (var i=0; i= 500)) { - // report internal errors right away - return callback(err) - } - - self._sync_package_with_uplinks(name, data, options, function(err, result, uplink_errors) { - if (err) return callback(err) - var whitelist = ['_rev', 'name', 'versions', 'dist-tags', 'readme'] - for (var i in result) { - if (whitelist.indexOf(i) === -1) delete result[i] - } - - if (self.config.ignore_latest_tag || !result['dist-tags'].latest) { - result['dist-tags'].latest = utils.semver_sort(Object.keys(result.versions)) - } - - for (var i in result['dist-tags']) { - if (Array.isArray(result['dist-tags'][i])) { - result['dist-tags'][i] = result['dist-tags'][i][result['dist-tags'][i].length-1] - if (result['dist-tags'][i] == null) delete result['dist-tags'][i] - } - } - - // npm can throw if this field doesn't exist - result._attachments = {} - - callback(null, result, uplink_errors) - }) - }) + if (typeof(options) === 'function') callback = options, options = {} + + var self = this + + self.local.get_package(name, options, function(err, data) { + if (err && (!err.status || err.status >= 500)) { + // report internal errors right away + return callback(err) + } + + self._sync_package_with_uplinks(name, data, options, function(err, result, uplink_errors) { + if (err) return callback(err) + var whitelist = [ '_rev', 'name', 'versions', 'dist-tags', 'readme' ] + for (var i in result) { + if (whitelist.indexOf(i) === -1) delete result[i] + } + + if (self.config.ignore_latest_tag || !result['dist-tags'].latest) { + result['dist-tags'].latest = Utils.semver_sort(Object.keys(result.versions)) + } + + for (var i in result['dist-tags']) { + if (Array.isArray(result['dist-tags'][i])) { + result['dist-tags'][i] = result['dist-tags'][i][result['dist-tags'][i].length-1] + if (result['dist-tags'][i] == null) delete result['dist-tags'][i] + } + } + + // npm can throw if this field doesn't exist + result._attachments = {} + + callback(null, result, uplink_errors) + }) + }) } // @@ -328,225 +326,222 @@ Storage.prototype.get_package = function(name, options, callback) { // Used storages: local && uplink (proxy_access) // Storage.prototype.search = function(startkey, options, callback) { - var self = this - var uplinks = [] - var i = 0 - - var uplinks - for (var p in self.uplinks) { - uplinks.push(p) - } - - function merge_with_local_packages(err, res, body) { - if (err) return callback(err) - var j = 0 - - self.local.get_recent_packages(startkey, function(err, list) { - if (err) return callback(err) - - var listL = list.length - - if (!listL) return callback(null, body) - - list.forEach(function(item) { - self.local.get_package(item.name, options, function(err, data) { - if (err) return callback(err) - - var versions = utils.semver_sort(Object.keys(data.versions)) - var latest = versions[versions.length - 1] - - if (data.versions[latest]) { - body[item.name] = { - name : data.versions[latest].name, - description : data.versions[latest].description, - 'dist-tags' : { - latest: latest - }, - maintainers : data.versions[latest].maintainers || [data.versions[latest]._npmUser].filter(Boolean), - readmeFilename: data.versions[latest].readmeFilename || '', - time : { - modified: new Date(item.time).toISOString() - }, - versions : {}, - repository : data.versions[latest].repository, - keywords : data.versions[latest].keywords - } - body[item.name].versions[latest] = 'latest' - } - - if (++j !== listL) { - return false - } - - return callback(null, body) - }) - }) - }) - } - - function remote_search() { - var uplink = self.uplinks[uplinks[i]] - if (!uplink) { - return merge_with_local_packages(null, null, {}) - } - self.uplinks[uplinks[i]].request({ - uri: options.req.url, - timeout: self.uplinks[p].timeout, - json: true - }, function(err, res, body) { - if (err || Math.floor(res.statusCode / 100) > 3) { - i++ - return remote_search() - } - return merge_with_local_packages(err, res, body) - }) - } - - remote_search() + var self = this + var uplinks = [] + var i = 0 + + var uplinks + for (var p in self.uplinks) { + uplinks.push(p) + } + + function merge_with_local_packages(err, res, body) { + if (err) return callback(err) + var j = 0 + + self.local.get_recent_packages(startkey, function(err, list) { + if (err) return callback(err) + + var listL = list.length + + if (!listL) return callback(null, body) + + list.forEach(function(item) { + self.local.get_package(item.name, options, function(err, data) { + if (err) return callback(err) + + var versions = Utils.semver_sort(Object.keys(data.versions)) + var latest = versions[versions.length - 1] + + if (data.versions[latest]) { + body[item.name] = { + name : data.versions[latest].name, + description : data.versions[latest].description, + 'dist-tags' : { latest: latest }, + maintainers : data.versions[latest].maintainers || [data.versions[latest]._npmUser].filter(Boolean), + readmeFilename: data.versions[latest].readmeFilename || '', + time : { + modified: new Date(item.time).toISOString() + }, + versions : {}, + repository : data.versions[latest].repository, + keywords : data.versions[latest].keywords + } + body[item.name].versions[latest] = 'latest' + } + + if (++j !== listL) { + return false + } + + return callback(null, body) + }) + }) + }) + } + + function remote_search() { + var uplink = self.uplinks[uplinks[i]] + if (!uplink) { + return merge_with_local_packages(null, null, {}) + } + self.uplinks[uplinks[i]].request({ + uri: options.req.url, + timeout: self.uplinks[p].timeout, + json: true, + }, function(err, res, body) { + if (err || Math.floor(res.statusCode / 100) > 3) { + i++ + return remote_search() + } + return merge_with_local_packages(err, res, body) + }) + } + + remote_search() } Storage.prototype.get_local = function(callback) { - var self = this - , locals = this.config.localList.get() - , packages = []; - - var getPackage = function(i) { - self.local.get_package(locals[i], function(err, info) { - if (!err) { - var latest = Array.isArray(info['dist-tags'].latest) - ? utils.semver_sort(info['dist-tags'].latest).pop() - : info['dist-tags'].latest - if (info.versions[latest]) { - packages.push(info.versions[latest]) - } else { - self.logger.warn({package: locals[i]}, 'package @{package} does not have a "latest" tag?') - } - } - - if (i >= locals.length - 1) { - callback(null, packages); - } else { - getPackage(i + 1); - } - }); - }; - - if(locals.length) { - getPackage(0); - } - else { - callback(null, []); - } -}; + var self = this + var locals = this.config.localList.get() + var packages = [] + + var getPackage = function(i) { + self.local.get_package(locals[i], function(err, info) { + if (!err) { + var latest = Array.isArray(info['dist-tags'].latest) + ? Utils.semver_sort(info['dist-tags'].latest).pop() + : info['dist-tags'].latest + if (info.versions[latest]) { + packages.push(info.versions[latest]) + } else { + self.logger.warn( { package: locals[i] } + , 'package @{package} does not have a "latest" tag?' ) + } + } + + if (i >= locals.length - 1) { + callback(null, packages) + } else { + getPackage(i + 1) + } + }) + } + + if (locals.length) { + getPackage(0) + } else { + callback(null, []) + } +} // function fetches package information from uplinks and synchronizes it with local data // if package is available locally, it MUST be provided in pkginfo // returns callback(err, result, uplink_errors) Storage.prototype._sync_package_with_uplinks = function(name, pkginfo, options, callback) { - var self = this - - if (!pkginfo) { - var exists = false - - pkginfo = { - name: name, - versions: {}, - 'dist-tags': {}, - _uplinks: {}, - } - } else { - var exists = true - } - - var uplinks = [] - for (var i in self.uplinks) { - if (self.config.proxy_access(name, i)) { - uplinks.push(self.uplinks[i]) - } - } - - async.map(uplinks, function(up, cb) { - var _options = Object.create(options) - if (utils.is_object(pkginfo._uplinks[up.upname])) { - var fetched = pkginfo._uplinks[up.upname].fetched - if (fetched && fetched > (Date.now() - up.maxage)) { - return cb() - } - - _options.etag = pkginfo._uplinks[up.upname].etag - } - - up.get_package(name, _options, function(err, up_res, etag) { - if (err && err.status === 304) - pkginfo._uplinks[up.upname].fetched = Date.now() - - if (err || !up_res) return cb(null, [err || Error('no data')]) - - try { - utils.validate_metadata(up_res, name) - } catch(err) { - self.logger.error({ - sub: 'out', - err: err, - }, 'package.json validating error @{!err.message}\n@{err.stack}') - return cb(null, [err]) - } - - pkginfo._uplinks[up.upname] = { - etag: etag, - fetched: Date.now() - } - - try { - Storage._merge_versions(pkginfo, up_res, self.config) - } catch(err) { - self.logger.error({ - sub: 'out', - err: err, - }, 'package.json parsing error @{!err.message}\n@{err.stack}') - return cb(null, [err]) - } - - // if we got to this point, assume that the correct package exists - // on the uplink - exists = true - cb() - }) - }, function(err, uplink_errors) { - assert(!err && Array.isArray(uplink_errors)) - - if (!exists) { - return callback( Error[404]('no such package available') - , null - , uplink_errors ) - } - - self.local.update_versions(name, pkginfo, function(err, pkginfo) { - if (err) return callback(err) - return callback(null, pkginfo, uplink_errors) - }) - }) + var self = this + + if (!pkginfo) { + var exists = false + + pkginfo = { + name : name, + versions : {}, + 'dist-tags' : {}, + _uplinks : {}, + } + } else { + var exists = true + } + + var uplinks = [] + for (var i in self.uplinks) { + if (self.config.proxy_access(name, i)) { + uplinks.push(self.uplinks[i]) + } + } + + async.map(uplinks, function(up, cb) { + var _options = Object.create(options) + if (Utils.is_object(pkginfo._uplinks[up.upname])) { + var fetched = pkginfo._uplinks[up.upname].fetched + if (fetched && fetched > (Date.now() - up.maxage)) { + return cb() + } + + _options.etag = pkginfo._uplinks[up.upname].etag + } + + up.get_package(name, _options, function(err, up_res, etag) { + if (err && err.status === 304) + pkginfo._uplinks[up.upname].fetched = Date.now() + + if (err || !up_res) return cb(null, [err || Error('no data')]) + + try { + Utils.validate_metadata(up_res, name) + } catch(err) { + self.logger.error({ + sub: 'out', + err: err, + }, 'package.json validating error @{!err.message}\n@{err.stack}') + return cb(null, [ err ]) + } + + pkginfo._uplinks[up.upname] = { + etag: etag, + fetched: Date.now() + } + + try { + Storage._merge_versions(pkginfo, up_res, self.config) + } catch(err) { + self.logger.error({ + sub: 'out', + err: err, + }, 'package.json parsing error @{!err.message}\n@{err.stack}') + return cb(null, [ err ]) + } + + // if we got to this point, assume that the correct package exists + // on the uplink + exists = true + cb() + }) + }, function(err, uplink_errors) { + assert(!err && Array.isArray(uplink_errors)) + + if (!exists) { + return callback( Error[404]('no such package available') + , null + , uplink_errors ) + } + + self.local.update_versions(name, pkginfo, function(err, pkginfo) { + if (err) return callback(err) + return callback(null, pkginfo, uplink_errors) + }) + }) } // function gets a local info and an info from uplinks and tries to merge it // exported for unit tests only Storage._merge_versions = function(local, up, config) { - // copy new versions to a cache - // NOTE: if a certain version was updated, we can't refresh it reliably - for (var i in up.versions) { - if (local.versions[i] == null) { - local.versions[i] = up.versions[i] - } - } - - // refresh dist-tags - for (var i in up['dist-tags']) { - var added = utils.tag_version(local, up['dist-tags'][i], i, config || {}) - if (i === 'latest' && added) { - // if remote has more fresh package, we should borrow its readme - local.readme = up.readme - } - } + // copy new versions to a cache + // NOTE: if a certain version was updated, we can't refresh it reliably + for (var i in up.versions) { + if (local.versions[i] == null) { + local.versions[i] = up.versions[i] + } + } + + // refresh dist-tags + for (var i in up['dist-tags']) { + var added = Utils.tag_version(local, up['dist-tags'][i], i, config || {}) + if (i === 'latest' && added) { + // if remote has more fresh package, we should borrow its readme + local.readme = up.readme + } + } } -module.exports = Storage diff --git a/lib/streams.js b/lib/streams.js index 13f6ab4171b9..9e9fc213cf2d 100644 --- a/lib/streams.js +++ b/lib/streams.js @@ -1,53 +1,60 @@ -var stream = require('stream') - , util = require('util') +var Stream = require('stream') +var Util = require('util') + +module.exports.ReadTarballStream = ReadTarball +module.exports.UploadTarballStream = UploadTarball // // This stream is used to read tarballs from repository // function ReadTarball(options) { - stream.PassThrough.call(this, options) + var self = new Stream.PassThrough(options) + self.__proto__ = ReadTarball.prototype + + // called when data is not needed anymore + add_abstract_method(self, 'abort') - // called when data is not needed anymore - add_abstract_method(this, 'abort') + return self } -util.inherits(ReadTarball, stream.PassThrough) -module.exports.ReadTarballStream = ReadTarball +Util.inherits(ReadTarball, Stream.PassThrough) // // This stream is used to upload tarballs to a repository // function UploadTarball(options) { - stream.PassThrough.call(this, options) + var self = new Stream.PassThrough(options) + self.__proto__ = UploadTarball.prototype + + // called when user closes connection before upload finishes + add_abstract_method(self, 'abort') - // called when user closes connection before upload finishes - add_abstract_method(this, 'abort') + // called when upload finishes successfully + add_abstract_method(self, 'done') - // called when upload finishes successfully - add_abstract_method(this, 'done') + return self } -util.inherits(UploadTarball, stream.PassThrough) -module.exports.UploadTarballStream = UploadTarball +Util.inherits(UploadTarball, Stream.PassThrough) // // This function intercepts abstract calls and replays them allowing // us to attach those functions after we are ready to do so // function add_abstract_method(self, name) { - self._called_methods = self._called_methods || {} - self.__defineGetter__(name, function() { - return function() { - self._called_methods[name] = true - } - }) - self.__defineSetter__(name, function(fn) { - delete self[name] - self[name] = fn - if (self._called_methods && self._called_methods[name]) { - delete self._called_methods[name] - self[name]() - } - }) + self._called_methods = self._called_methods || {} + self.__defineGetter__(name, function() { + return function() { + self._called_methods[name] = true + } + }) + self.__defineSetter__(name, function(fn) { + delete self[name] + self[name] = fn + if (self._called_methods && self._called_methods[name]) { + delete self._called_methods[name] + self[name]() + } + }) } diff --git a/lib/up-storage.js b/lib/up-storage.js index 2634999451fa..69f67c4f498b 100644 --- a/lib/up-storage.js +++ b/lib/up-storage.js @@ -1,345 +1,346 @@ -var URL = require('url') - , request = require('request') - , Stream = require('stream') - , zlib = require('zlib') - , Error = require('http-errors') - , mystreams = require('./streams') - , Logger = require('./logger') - , utils = require('./utils') - , parse_interval = require('./config').parse_interval - , encode = encodeURIComponent +var Error = require('http-errors') +var request = require('request') +var Stream = require('stream') +var URL = require('url') +var zlib = require('zlib') +var parse_interval = require('./config').parse_interval +var Logger = require('./logger') +var MyStreams = require('./streams') +var Utils = require('./utils') +var encode = encodeURIComponent + +module.exports = Storage // // Implements Storage interface // (same for storage.js, local-storage.js, up-storage.js) // function Storage(config, mainconfig) { - if (!(this instanceof Storage)) return new Storage(config) - this.config = config - this.failed_requests = 0 - this.userAgent = mainconfig.user_agent - this.ca = config.ca - this.logger = Logger.logger.child({sub: 'out'}) - this.server_id = mainconfig.server_id - - this.url = URL.parse(this.config.url) - if (this.url.hostname === 'registry.npmjs.org') { - // npm registry is too slow working with ssl :( - /*if (this.config._autogenerated) { - // encrypt all the things! - this.url.protocol = 'https' - this.config.url = URL.format(this.url) - }*/ - } - - _setupProxy.call(this, this.url.hostname, config, mainconfig, this.url.protocol === 'https:') - - this.config.url = this.config.url.replace(/\/$/, '') - if (Number(this.config.timeout) >= 1000) { - this.logger.warn('Too big timeout value: ' + this.config.timeout + '\nWe changed time format to nginx-like one\n(see http://wiki.nginx.org/ConfigNotation)\nso please update your config accordingly') - } - - // a bunch of different configurable timers - this.maxage = parse_interval(config_get('maxage' , '2m' )) - this.timeout = parse_interval(config_get('timeout' , '30s')) - this.max_fails = Number(config_get('max_fails' , 2 )) - this.fail_timeout = parse_interval(config_get('fail_timeout', '5m' )) - return this - - // just a helper (`config[key] || default` doesn't work because of zeroes) - function config_get(key, def) { - return config[key] != null ? config[key] : def - } + var self = Object.create(Storage.prototype) + self.config = config + self.failed_requests = 0 + self.userAgent = mainconfig.user_agent + self.ca = config.ca + self.logger = Logger.logger.child({sub: 'out'}) + self.server_id = mainconfig.server_id + + self.url = URL.parse(self.config.url) + if (self.url.hostname === 'registry.npmjs.org') { + // npm registry is too slow working with ssl :( + /*if (self.config._autogenerated) { + // encrypt all the things! + self.url.protocol = 'https' + self.config.url = URL.format(self.url) + }*/ + } + + _setupProxy.call(self, self.url.hostname, config, mainconfig, self.url.protocol === 'https:') + + self.config.url = self.config.url.replace(/\/$/, '') + if (Number(self.config.timeout) >= 1000) { + self.logger.warn([ 'Too big timeout value: ' + self.config.timeout, + 'We changed time format to nginx-like one', + '(see http://wiki.nginx.org/ConfigNotation)', + 'so please update your config accordingly' ].join('\n')) + } + + // a bunch of different configurable timers + self.maxage = parse_interval(config_get('maxage' , '2m' )) + self.timeout = parse_interval(config_get('timeout' , '30s')) + self.max_fails = Number(config_get('max_fails' , 2 )) + self.fail_timeout = parse_interval(config_get('fail_timeout', '5m' )) + return self + + // just a helper (`config[key] || default` doesn't work because of zeroes) + function config_get(key, def) { + return config[key] != null ? config[key] : def + } } function _setupProxy(hostname, config, mainconfig, isHTTPS) { - var no_proxy - var proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy' - - // get http_proxy and no_proxy configs - if (proxy_key in config) { - this.proxy = config[proxy_key] - } else if (proxy_key in mainconfig) { - this.proxy = mainconfig[proxy_key] - } - if ('no_proxy' in config) { - no_proxy = config.no_proxy - } else if ('no_proxy' in mainconfig) { - no_proxy = mainconfig.no_proxy - } - - // use wget-like algorithm to determine if proxy shouldn't be used - if (hostname[0] !== '.') hostname = '.' + hostname - if (typeof(no_proxy) === 'string' && no_proxy.length) { - no_proxy = no_proxy.split(',') - } - if (Array.isArray(no_proxy)) { - for (var i=0; i= this.max_fails && Math.abs(Date.now() - this.last_request_time) < this.fail_timeout) { - return false - } else { - return true - } - } else { - if (alive) { - if (this.failed_requests >= this.max_fails) { - this.logger.warn({host: this.url.host}, 'host @{host} is back online') - } - this.failed_requests = 0 - } else { - this.failed_requests++ - if (this.failed_requests === this.max_fails) { - this.logger.warn({host: this.url.host}, 'host @{host} is now offline') - } - } - this.last_request_time = Date.now() - } + if (arguments.length === 0) { + if (this.failed_requests >= this.max_fails + && Math.abs(Date.now() - this.last_request_time) < this.fail_timeout) { + return false + } else { + return true + } + } else { + if (alive) { + if (this.failed_requests >= this.max_fails) { + this.logger.warn({ host: this.url.host }, 'host @{host} is back online') + } + this.failed_requests = 0 + } else { + this.failed_requests++ + if (this.failed_requests === this.max_fails) { + this.logger.warn({ host: this.url.host }, 'host @{host} is now offline') + } + } + this.last_request_time = Date.now() + } } Storage.prototype.can_fetch_url = function(url) { - url = URL.parse(url) + url = URL.parse(url) - return url.protocol === this.url.protocol - && url.host === this.url.host - && url.path.indexOf(this.url.path) === 0 + return url.protocol === this.url.protocol + && url.host === this.url.host + && url.path.indexOf(this.url.path) === 0 } Storage.prototype.get_package = function(name, options, callback) { - if (typeof(options) === 'function') callback = options, options = {} - - var headers = {} - if (options.etag) { - headers['If-None-Match'] = options.etag - headers['Accept'] = 'application/octet-stream' - } - this._add_proxy_headers(options.req, headers) - - this.request({ - uri: '/' + encode(name), - json: true, - headers: headers, - }, function(err, res, body) { - if (err) return callback(err) - if (res.statusCode === 404) { - return callback(Error[404]("package doesn't exist on uplink")) - } - if (!(res.statusCode >= 200 && res.statusCode < 300)) { - var error = Error('bad status code: ' + res.statusCode) - error.remoteStatus = res.statusCode - return callback(error) - } - callback(null, body, res.headers.etag) - }) + if (typeof(options) === 'function') callback = options, options = {} + + var headers = {} + if (options.etag) { + headers['If-None-Match'] = options.etag + headers['Accept'] = 'application/octet-stream' + } + this._add_proxy_headers(options.req, headers) + + this.request({ + uri : '/' + encode(name), + json : true, + headers : headers, + }, function(err, res, body) { + if (err) return callback(err) + if (res.statusCode === 404) { + return callback( Error[404]("package doesn't exist on uplink") ) + } + if (!(res.statusCode >= 200 && res.statusCode < 300)) { + var error = Error('bad status code: ' + res.statusCode) + error.remoteStatus = res.statusCode + return callback(error) + } + callback(null, body, res.headers.etag) + }) } Storage.prototype.get_tarball = function(name, options, filename) { - if (!options) options = {} - return this.get_url(this.config.url + '/' + name + '/-/' + filename) + if (!options) options = {} + return this.get_url(this.config.url + '/' + name + '/-/' + filename) } Storage.prototype.get_url = function(url) { - var stream = new mystreams.ReadTarballStream() - stream.abort = function() {} - var current_length = 0, expected_length - - var rstream = this.request({ - uri_full: url, - encoding: null, - headers: { - Accept: 'application/octet-stream', - }, - }) - - rstream.on('response', function(res) { - if (res.statusCode === 404) { - return stream.emit('error', Error[404]("file doesn't exist on uplink")) - } - if (!(res.statusCode >= 200 && res.statusCode < 300)) { - return stream.emit('error', Error('bad uplink status code: ' + res.statusCode)) - } - if (res.headers['content-length']) { - expected_length = res.headers['content-length'] - stream.emit('content-length', res.headers['content-length']) - } - - rstream.pipe(stream) - }) - - rstream.on('error', function(err) { - stream.emit('error', err) - }) - rstream.on('data', function(d) { - current_length += d.length - }) - rstream.on('end', function(d) { - if (d) current_length += d.length - if (expected_length && current_length != expected_length) - stream.emit('error', Error('content length mismatch')) - }) - return stream + var stream = MyStreams.ReadTarballStream() + stream.abort = function() {} + var current_length = 0, expected_length + + var rstream = this.request({ + uri_full: url, + encoding: null, + headers: { Accept: 'application/octet-stream' }, + }) + + rstream.on('response', function(res) { + if (res.statusCode === 404) { + return stream.emit('error', Error[404]("file doesn't exist on uplink")) + } + if (!(res.statusCode >= 200 && res.statusCode < 300)) { + return stream.emit('error', Error('bad uplink status code: ' + res.statusCode)) + } + if (res.headers['content-length']) { + expected_length = res.headers['content-length'] + stream.emit('content-length', res.headers['content-length']) + } + + rstream.pipe(stream) + }) + + rstream.on('error', function(err) { + stream.emit('error', err) + }) + rstream.on('data', function(d) { + current_length += d.length + }) + rstream.on('end', function(d) { + if (d) current_length += d.length + if (expected_length && current_length != expected_length) + stream.emit('error', Error('content length mismatch')) + }) + return stream } Storage.prototype._add_proxy_headers = function(req, headers) { - if (req) { - headers['X-Forwarded-For'] = ( - (req && req.headers['x-forwarded-for']) ? - req.headers['x-forwarded-for'] + ', ' : - '' - ) + req.connection.remoteAddress - } - - // always attach Via header to avoid loops, even if we're not proxying - headers['Via'] = - (req && req.headers['via']) ? - req.headers['via'] + ', ' : - '' - - headers['Via'] += '1.1 ' + this.server_id + ' (Sinopia)' + if (req) { + headers['X-Forwarded-For'] = ( + req && req.headers['x-forwarded-for'] + ? req.headers['x-forwarded-for'] + ', ' + : '' + ) + req.connection.remoteAddress + } + + // always attach Via header to avoid loops, even if we're not proxying + headers['Via'] = + req && req.headers['via'] + ? req.headers['via'] + ', ' + : '' + + headers['Via'] += '1.1 ' + this.server_id + ' (Sinopia)' } -module.exports = Storage - diff --git a/lib/utils.js b/lib/utils.js index 41178796c962..3de2f3fe53df 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,149 +1,149 @@ var assert = require('assert') - , semver = require('semver') - , Logger = require('./logger') - , URL = require('url') +var Semver = require('semver') +var URL = require('url') +var Logger = require('./logger') // from normalize-package-data/lib/fixer.js module.exports.validate_name = function(name) { - if (typeof(name) !== 'string') return false - name = name.toLowerCase() - if ( - // all URL-safe characters and "@" for issue #75 - !name.match(/^[-a-zA-Z0-9_.!~*'()@]+$/) || - name.charAt(0) === '.' || // ".bin", etc. - name.charAt(0) === '-' || // "-" is reserved by couchdb - name === 'node_modules' || - name === '__proto__' || - name === 'package.json' || - name === 'favicon.ico' - ) { - return false - } else { - return true - } + if (typeof(name) !== 'string') return false + name = name.toLowerCase() + + // all URL-safe characters and "@" for issue #75 + if (!name.match(/^[-a-zA-Z0-9_.!~*'()@]+$/) + || name.charAt(0) === '.' // ".bin", etc. + || name.charAt(0) === '-' // "-" is reserved by couchdb + || name === 'node_modules' + || name === '__proto__' + || name === 'package.json' + || name === 'favicon.ico' + ) { + return false + } else { + return true + } } module.exports.is_object = function(obj) { - return typeof(obj) === 'object' && obj !== null && !Array.isArray(obj) + return typeof(obj) === 'object' && obj !== null && !Array.isArray(obj) } module.exports.validate_metadata = function(object, name) { - assert(module.exports.is_object(object), 'not a json object') - assert.equal(object.name, name) + assert(module.exports.is_object(object), 'not a json object') + assert.equal(object.name, name) - if (!module.exports.is_object(object['dist-tags'])) { - object['dist-tags'] = {} - } + if (!module.exports.is_object(object['dist-tags'])) { + object['dist-tags'] = {} + } - if (!module.exports.is_object(object['versions'])) { - object['versions'] = {} - } + if (!module.exports.is_object(object['versions'])) { + object['versions'] = {} + } - return object + return object } module.exports.parse_tarball_url = function(_url) { - var url = URL.parse(_url) - - var path = url.path.replace(/^\//, '').split('/') - if (path.length >= 3 && path[path.length-2] === '-') { - var filename = path.pop() - , pkgpath = '/' + filename // tarball name - pkgpath = '/' + path.pop() + pkgpath // "-" - pkgpath = '/' + path.pop() + pkgpath // package.name - } else { - return null - } - - return { - protocol: url.protocol, - host: url.host, - prepath: '/' + path.join('/'), - pkgpath: pkgpath, - filename: filename, - } + var url = URL.parse(_url) + + var path = url.path.replace(/^\//, '').split('/') + if (path.length >= 3 && path[path.length-2] === '-') { + var filename = path.pop() + var pkgpath = '/' + filename // tarball name + pkgpath = '/' + path.pop() + pkgpath // "-" + pkgpath = '/' + path.pop() + pkgpath // package.name + } else { + return null + } + + return { + protocol : url.protocol, + host : url.host, + prepath : '/' + path.join('/'), + pkgpath : pkgpath, + filename : filename, + } } module.exports.filter_tarball_urls = function(pkg, req, config) { - function filter(_url) { - if (!req.headers.host) return _url - - var url = module.exports.parse_tarball_url(_url) - // weird url, just return it - if (url == null) return _url - - if (config.url_prefix != null) { - var result = config.url_prefix.replace(/\/$/, '') - } else { - var result = req.protocol + '://' + req.headers.host - } - - return result + url.pkgpath - } - - for (var ver in pkg.versions) { - var dist = pkg.versions[ver].dist - if (dist != null && dist.tarball != null) { - //dist.__sinopia_orig_tarball = dist.tarball - dist.tarball = filter(dist.tarball) - } - } - return pkg + function filter(_url) { + if (!req.headers.host) return _url + + var url = module.exports.parse_tarball_url(_url) + // weird url, just return it + if (url == null) return _url + + if (config.url_prefix != null) { + var result = config.url_prefix.replace(/\/$/, '') + } else { + var result = req.protocol + '://' + req.headers.host + } + + return result + url.pkgpath + } + + for (var ver in pkg.versions) { + var dist = pkg.versions[ver].dist + if (dist != null && dist.tarball != null) { + //dist.__sinopia_orig_tarball = dist.tarball + dist.tarball = filter(dist.tarball) + } + } + return pkg } function can_add_tag(tag, config) { - if (!tag) return false - if (tag === 'latest' && config.ignore_latest_tag) return false - return true + if (!tag) return false + if (tag === 'latest' && config.ignore_latest_tag) return false + return true } module.exports.tag_version = function(data, version, tag, config) { - if (!can_add_tag(tag, config)) return false - - switch(typeof(data['dist-tags'][tag])) { - case 'string': - data['dist-tags'][tag] = [data['dist-tags'][tag]] - break - case 'object': // array - break - default: - data['dist-tags'][tag] = [] - } - if (data['dist-tags'][tag].indexOf(version) === -1) { - data['dist-tags'][tag].push(version) - data['dist-tags'][tag] = module.exports.semver_sort(data['dist-tags'][tag]) - return data['dist-tags'][tag][data['dist-tags'][tag].length - 1] === version - } - return false + if (!can_add_tag(tag, config)) return false + + switch (typeof(data['dist-tags'][tag])) { + case 'string': + data['dist-tags'][tag] = [ data['dist-tags'][tag] ] + break + case 'object': // array + break + default: + data['dist-tags'][tag] = [] + } + if (data['dist-tags'][tag].indexOf(version) === -1) { + data['dist-tags'][tag].push(version) + data['dist-tags'][tag] = module.exports.semver_sort(data['dist-tags'][tag]) + return data['dist-tags'][tag][data['dist-tags'][tag].length - 1] === version + } + return false } // gets version from a package object taking into account semver weirdness module.exports.get_version = function(object, version) { - if (object.versions[version] != null) return object.versions[version] - - try { - version = semver.parse(version, true) - for (var k in object.versions) { - if (version.compare(semver.parse(k, true)) === 0) { - return object.versions[k] - } - } - } catch(err) { - return undefined - } + if (object.versions[version] != null) return object.versions[version] + + try { + version = Semver.parse(version, true) + for (var k in object.versions) { + if (version.compare(Semver.parse(k, true)) === 0) { + return object.versions[k] + } + } + } catch (err) { + return undefined + } } // function filters out bad semver versions and sorts the array module.exports.semver_sort = function semver_sort(array) { - return array - .filter(function(x) { - if (!semver.parse(x, true)) { - Logger.logger.warn({ver: x}, 'ignoring bad version @{ver}') - return false - } - return true - }) - .sort(semver.compareLoose) - .map(String) + return array + .filter(function(x) { + if (!Semver.parse(x, true)) { + Logger.logger.warn( {ver: x}, 'ignoring bad version @{ver}' ) + return false + } + return true + }) + .sort(Semver.compareLoose) + .map(String) } diff --git a/test/functional/addtag.js b/test/functional/addtag.js index 593f5658bf3b..bdbd79f49546 100644 --- a/test/functional/addtag.js +++ b/test/functional/addtag.js @@ -1,58 +1,58 @@ var assert = require('assert') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - - it('add tag - 404', function(cb) { - server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) - - describe('addtag', function() { - before(function(cb) { - server.put_package('testpkg-tag', eval( - '(' + readfile('fixtures/publish.json5') - .toString('utf8') - .replace(/__NAME__/g, 'testpkg-tag') - .replace(/__VERSION__/g, '0.0.1') - + ')' - ), function(res, body) { - assert.equal(res.statusCode, 201) - cb() - }) - }) - - it('add testpkg-tag', function(){}) - - it('add tag - bad ver', function(cb) { - server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('version doesn\'t exist')) - cb() - }) - }) - - it('add tag - bad tag', function(cb) { - server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x', function(res, body) { - assert.equal(res.statusCode, 403) - assert(~body.error.indexOf('invalid tag')) - cb() - }) - }) - - it('add tag - good', function(cb) { - server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('tagged')) - cb() - }) - }) - }) + var server = process.server + + it('add tag - 404', function(cb) { + server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) + + describe('addtag', function() { + before(function(cb) { + server.put_package('testpkg-tag', eval( + '(' + readfile('fixtures/publish.json5') + .toString('utf8') + .replace(/__NAME__/g, 'testpkg-tag') + .replace(/__VERSION__/g, '0.0.1') + + ')' + ), function(res, body) { + assert.equal(res.statusCode, 201) + cb() + }) + }) + + it('add testpkg-tag', function(){}) + + it('add tag - bad ver', function(cb) { + server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('version doesn\'t exist')) + cb() + }) + }) + + it('add tag - bad tag', function(cb) { + server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x', function(res, body) { + assert.equal(res.statusCode, 403) + assert(~body.error.indexOf('invalid tag')) + cb() + }) + }) + + it('add tag - good', function(cb) { + server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('tagged')) + cb() + }) + }) + }) } diff --git a/test/functional/adduser.js b/test/functional/adduser.js index ff807ca6da4c..a81f6c4fe53d 100644 --- a/test/functional/adduser.js +++ b/test/functional/adduser.js @@ -2,35 +2,35 @@ var assert = require('assert') var Server = require('./lib/server') module.exports = function() { - var server = new Server('http://localhost:55551/') + var server = new Server('http://localhost:55551/') - describe('adduser', function() { - var user = String(Math.random()) - var pass = String(Math.random()) - before(function(cb) { - server.auth(user, pass, function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok.match(/user .* created/)) - cb() - }) - }) + describe('adduser', function() { + var user = String(Math.random()) + var pass = String(Math.random()) + before(function(cb) { + server.auth(user, pass, function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok.match(/user .* created/)) + cb() + }) + }) - it('creating new user', function(){}) + it('creating new user', function(){}) - it('should log in', function(cb) { - server.auth(user, pass, function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok.match(/you are authenticated as/)) - cb() - }) - }) + it('should log in', function(cb) { + server.auth(user, pass, function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok.match(/you are authenticated as/)) + cb() + }) + }) - it('should not register more users', function(cb) { - server.auth(String(Math.random()), String(Math.random()), function(res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/maximum amount of users reached/)) - cb() - }) - }) - }) + it('should not register more users', function(cb) { + server.auth(String(Math.random()), String(Math.random()), function(res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/maximum amount of users reached/)) + cb() + }) + }) + }) } diff --git a/test/functional/basic.js b/test/functional/basic.js index d285770d3856..8aedb89aef21 100644 --- a/test/functional/basic.js +++ b/test/functional/basic.js @@ -1,133 +1,133 @@ require('./lib/startup') var assert = require('assert') - , async = require('async') - , crypto = require('crypto') +var async = require('async') +var crypto = require('crypto') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var server2 = process.server2 - - it('trying to fetch non-existent package', function(cb) { - server.get_package('testpkg', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) - - describe('testpkg', function() { - before(server.add_package.bind(server, 'testpkg')) - - it('creating new package', function(){/* test for before() */}) - - it('downloading non-existent tarball', function(cb) { - server.get_tarball('testpkg', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such file')) - cb() - }) - }) - - it('uploading incomplete tarball', function(cb) { - server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000, function(res, body) { - cb() - }) - }) - - describe('tarball', function() { - before(function(cb) { - server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok) - cb() - }) - }) - - it('uploading new tarball', function(){/* test for before() */}) - - it('downloading newly created tarball', function(cb) { - server.get_tarball('testpkg', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 200) - assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) - cb() - }) - }) - - it('uploading new package version (bad sha)', function(cb) { - var pkg = require('./lib/package')('testpkg') - pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex') - server.put_version('testpkg', '0.0.1', pkg, function(res, body) { - assert.equal(res.statusCode, 400) - assert(~body.error.indexOf('shasum error')) - cb() - }) - }) - - describe('version', function() { - before(function(cb) { - var pkg = require('./lib/package')('testpkg') - pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') - server.put_version('testpkg', '0.0.1', pkg, function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('published')) - cb() - }) - }) - - it('uploading new package version', function(){/* test for before() */}) - - it('downloading newly created package', function(cb) { - server.get_package('testpkg', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.name, 'testpkg') - assert.equal(body.versions['0.0.1'].name, 'testpkg') - assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah') - assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) - cb() - }) - }) - - it('downloading package via server2', function(cb) { - server2.get_package('testpkg', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.name, 'testpkg') - assert.equal(body.versions['0.0.1'].name, 'testpkg') - assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah') - assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) - cb() - }) - }) - }) - }) - }) - - it('uploading new package version for bad pkg', function(cb) { - server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'), function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) - - it('doubleerr test', function(cb) { - server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'), function(res, body) { - assert.equal(res.statusCode, 404) - assert(body.error) - cb() - }) - }) - - it('publishing package / bad ro uplink', function(cb) { - server.put_package('baduplink', require('./lib/package')('baduplink'), function(res, body) { - assert.equal(res.statusCode, 503) - assert(~body.error.indexOf('one of the uplinks is down, refuse to publish')) - cb() - }) - }) + var server = process.server + var server2 = process.server2 + + it('trying to fetch non-existent package', function(cb) { + server.get_package('testpkg', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) + + describe('testpkg', function() { + before(server.add_package.bind(server, 'testpkg')) + + it('creating new package', function(){/* test for before() */}) + + it('downloading non-existent tarball', function(cb) { + server.get_tarball('testpkg', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such file')) + cb() + }) + }) + + it('uploading incomplete tarball', function(cb) { + server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000, function(res, body) { + cb() + }) + }) + + describe('tarball', function() { + before(function(cb) { + server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok) + cb() + }) + }) + + it('uploading new tarball', function(){/* test for before() */}) + + it('downloading newly created tarball', function(cb) { + server.get_tarball('testpkg', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 200) + assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) + cb() + }) + }) + + it('uploading new package version (bad sha)', function(cb) { + var pkg = require('./lib/package')('testpkg') + pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex') + server.put_version('testpkg', '0.0.1', pkg, function(res, body) { + assert.equal(res.statusCode, 400) + assert(~body.error.indexOf('shasum error')) + cb() + }) + }) + + describe('version', function() { + before(function(cb) { + var pkg = require('./lib/package')('testpkg') + pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') + server.put_version('testpkg', '0.0.1', pkg, function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('published')) + cb() + }) + }) + + it('uploading new package version', function(){/* test for before() */}) + + it('downloading newly created package', function(cb) { + server.get_package('testpkg', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'testpkg') + assert.equal(body.versions['0.0.1'].name, 'testpkg') + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah') + assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) + cb() + }) + }) + + it('downloading package via server2', function(cb) { + server2.get_package('testpkg', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'testpkg') + assert.equal(body.versions['0.0.1'].name, 'testpkg') + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah') + assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) + cb() + }) + }) + }) + }) + }) + + it('uploading new package version for bad pkg', function(cb) { + server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'), function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) + + it('doubleerr test', function(cb) { + server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 404) + assert(body.error) + cb() + }) + }) + + it('publishing package / bad ro uplink', function(cb) { + server.put_package('baduplink', require('./lib/package')('baduplink'), function(res, body) { + assert.equal(res.statusCode, 503) + assert(~body.error.indexOf('one of the uplinks is down, refuse to publish')) + cb() + }) + }) } diff --git a/test/functional/gh29.js b/test/functional/gh29.js index 3847db5d8519..fcb0cda941eb 100644 --- a/test/functional/gh29.js +++ b/test/functional/gh29.js @@ -1,75 +1,74 @@ var assert = require('assert') - , crypto = require('crypto') - , ex = module.exports +var crypto = require('crypto') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - it('downloading non-existent tarball #1 / srv2', function(cb) { - server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) + it('downloading non-existent tarball #1 / srv2', function(cb) { + server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) - describe('pkg-gh29', function() { - before(function(cb) { - server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('created new package')) - cb() - }) - }) + describe('pkg-gh29', function() { + before(function(cb) { + server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('created new package')) + cb() + }) + }) - it('creating new package / srv1', function(){}) + it('creating new package / srv1', function(){}) - it('downloading non-existent tarball #2 / srv2', function(cb) { - server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such file')) - cb() - }) - }) + it('downloading non-existent tarball #2 / srv2', function(cb) { + server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such file')) + cb() + }) + }) - describe('tarball', function() { - before(function(cb) { - server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok) - cb() - }) - }) + describe('tarball', function() { + before(function(cb) { + server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok) + cb() + }) + }) - it('uploading new tarball / srv1', function(){}) + it('uploading new tarball / srv1', function(){}) - describe('pkg version', function() { - before(function(cb) { - var pkg = require('./lib/package')('testpkg-gh29') - pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') - server.put_version('testpkg-gh29', '0.0.1', pkg, function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('published')) - cb() - }) - }) + describe('pkg version', function() { + before(function(cb) { + var pkg = require('./lib/package')('testpkg-gh29') + pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') + server.put_version('testpkg-gh29', '0.0.1', pkg, function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('published')) + cb() + }) + }) - it('uploading new package version / srv1', function(){}) + it('uploading new package version / srv1', function(){}) - it('downloading newly created tarball / srv2', function(cb) { - server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 200) - assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) - cb() - }) - }) - }) - }) - }) + it('downloading newly created tarball / srv2', function(cb) { + server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 200) + assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) + cb() + }) + }) + }) + }) + }) } diff --git a/test/functional/gzip.js b/test/functional/gzip.js index 7d7ca257ef17..23164a097bb3 100644 --- a/test/functional/gzip.js +++ b/test/functional/gzip.js @@ -1,93 +1,93 @@ require('./lib/startup') var assert = require('assert') - , async = require('async') - , crypto = require('crypto') +var async = require('async') +var crypto = require('crypto') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var express = process.express + var server = process.server + var express = process.express - describe('testexp_gzip', function() { - before(function() { - express.get('/testexp_gzip', function(req, res) { - var x = eval( - '(' + readfile('fixtures/publish.json5') - .toString('utf8') - .replace(/__NAME__/g, 'testexp_gzip') - .replace(/__VERSION__/g, '0.0.1') - + ')' - ) + describe('testexp_gzip', function() { + before(function() { + express.get('/testexp_gzip', function(req, res) { + var x = eval( + '(' + readfile('fixtures/publish.json5') + .toString('utf8') + .replace(/__NAME__/g, 'testexp_gzip') + .replace(/__VERSION__/g, '0.0.1') + + ')' + ) - // overcoming compress threshold - x.versions['0.0.2'] = x.versions['0.0.1'] - x.versions['0.0.3'] = x.versions['0.0.1'] - x.versions['0.0.4'] = x.versions['0.0.1'] - x.versions['0.0.5'] = x.versions['0.0.1'] - x.versions['0.0.6'] = x.versions['0.0.1'] - x.versions['0.0.7'] = x.versions['0.0.1'] - x.versions['0.0.8'] = x.versions['0.0.1'] - x.versions['0.0.9'] = x.versions['0.0.1'] + // overcoming compress threshold + x.versions['0.0.2'] = x.versions['0.0.1'] + x.versions['0.0.3'] = x.versions['0.0.1'] + x.versions['0.0.4'] = x.versions['0.0.1'] + x.versions['0.0.5'] = x.versions['0.0.1'] + x.versions['0.0.6'] = x.versions['0.0.1'] + x.versions['0.0.7'] = x.versions['0.0.1'] + x.versions['0.0.8'] = x.versions['0.0.1'] + x.versions['0.0.9'] = x.versions['0.0.1'] - require('zlib').gzip(JSON.stringify(x), function(err, buf) { - assert(!err) - assert.equal(req.headers['accept-encoding'], 'gzip') - res.header('content-encoding', 'gzip') - res.send(buf) - }) - }) + require('zlib').gzip(JSON.stringify(x), function(err, buf) { + assert(!err) + assert.equal(req.headers['accept-encoding'], 'gzip') + res.header('content-encoding', 'gzip') + res.send(buf) + }) + }) - express.get('/testexp_baddata', function(req, res) { - assert.equal(req.headers['accept-encoding'], 'gzip') - res.header('content-encoding', 'gzip') - res.send(new Buffer([1,2,3,4,5,6,7,7,6,5,4,3,2,1])) - }) - }) + express.get('/testexp_baddata', function(req, res) { + assert.equal(req.headers['accept-encoding'], 'gzip') + res.header('content-encoding', 'gzip') + res.send(new Buffer([1,2,3,4,5,6,7,7,6,5,4,3,2,1])) + }) + }) - it('should not fail on bad gzip', function(cb) { - server.get_package('testexp_baddata', function(res, body) { - assert.equal(res.statusCode, 404) - cb() - }) - }) + it('should not fail on bad gzip', function(cb) { + server.get_package('testexp_baddata', function(res, body) { + assert.equal(res.statusCode, 404) + cb() + }) + }) - it('should understand gzipped data from uplink', function(cb) { - server.get_package('testexp_gzip', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(res.headers['content-encoding'], undefined) - assert.equal(body.name, 'testexp_gzip') - assert.equal(Object.keys(body.versions).length, 9) - cb() - }) - }) + it('should understand gzipped data from uplink', function(cb) { + server.get_package('testexp_gzip', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(res.headers['content-encoding'], undefined) + assert.equal(body.name, 'testexp_gzip') + assert.equal(Object.keys(body.versions).length, 9) + cb() + }) + }) - it('should serve gzipped data', function(cb) { - server.request({ - uri: '/testexp_gzip', - encoding: null, - headers: { - 'Accept-encoding': 'gzip', - }, - json: false, - }, function(err, res, body) { - assert.equal(res.statusCode, 200) - assert.equal(res.headers['content-encoding'], 'gzip') - assert.throws(function() { - JSON.parse(body.toString('utf8')) - }) - require('zlib').gunzip(body, function(err, buf) { - assert(!err) - body = JSON.parse(buf) - assert.equal(body.name, 'testexp_gzip') - assert.equal(Object.keys(body.versions).length, 9) - cb() - }) - }) - }) - }) + it('should serve gzipped data', function(cb) { + server.request({ + uri: '/testexp_gzip', + encoding: null, + headers: { + 'Accept-encoding': 'gzip', + }, + json: false, + }, function(err, res, body) { + assert.equal(res.statusCode, 200) + assert.equal(res.headers['content-encoding'], 'gzip') + assert.throws(function() { + JSON.parse(body.toString('utf8')) + }) + require('zlib').gunzip(body, function(err, buf) { + assert(!err) + body = JSON.parse(buf) + assert.equal(body.name, 'testexp_gzip') + assert.equal(Object.keys(body.versions).length, 9) + cb() + }) + }) + }) + }) } diff --git a/test/functional/incomplete.js b/test/functional/incomplete.js index 0d3cb5c38296..87f9e66724a4 100644 --- a/test/functional/incomplete.js +++ b/test/functional/incomplete.js @@ -1,67 +1,66 @@ var assert = require('assert') - , ex = module.exports module.exports = function() { - var server = process.server - var express = process.express + var server = process.server + var express = process.express - describe('Incomplete', function() { - var on_tarball + describe('Incomplete', function() { + var on_tarball - before(function() { - express.get('/testexp-incomplete', function(_, res) { - res.send({ - "name": "testexp-incomplete", - "versions": { - "0.1.0": { - "name": "testexp_tags", - "version": "0.1.0", - "dist": { - "shasum": "fake", - "tarball": "http://localhost:55550/testexp-incomplete/-/content-length.tar.gz" - } - }, - "0.1.1": { - "name": "testexp_tags", - "version": "0.1.1", - "dist": { - "shasum": "fake", - "tarball": "http://localhost:55550/testexp-incomplete/-/chunked.tar.gz" - } - } - } - }) - }) - }) + before(function() { + express.get('/testexp-incomplete', function(_, res) { + res.send({ + "name": "testexp-incomplete", + "versions": { + "0.1.0": { + "name": "testexp_tags", + "version": "0.1.0", + "dist": { + "shasum": "fake", + "tarball": "http://localhost:55550/testexp-incomplete/-/content-length.tar.gz" + } + }, + "0.1.1": { + "name": "testexp_tags", + "version": "0.1.1", + "dist": { + "shasum": "fake", + "tarball": "http://localhost:55550/testexp-incomplete/-/chunked.tar.gz" + } + } + } + }) + }) + }) - ;['content-length', 'chunked'].forEach(function(type) { - it('should not store tarballs / ' + type, function(_cb) { - var called - express.get('/testexp-incomplete/-/'+type+'.tar.gz', function(_, res) { - if (called) return res.socket.destroy() - called = true - if (type !== 'chunked') res.header('content-length', 1e6) - res.write('test test test\n') - setTimeout(function() { - res.socket.write('200\nsss\n') - res.socket.destroy() - cb() - }, 10) - }) + ;['content-length', 'chunked'].forEach(function(type) { + it('should not store tarballs / ' + type, function(_cb) { + var called + express.get('/testexp-incomplete/-/'+type+'.tar.gz', function(_, res) { + if (called) return res.socket.destroy() + called = true + if (type !== 'chunked') res.header('content-length', 1e6) + res.write('test test test\n') + setTimeout(function() { + res.socket.write('200\nsss\n') + res.socket.destroy() + cb() + }, 10) + }) - server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) { - if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6) - assert(body.match(/test test test/)) - }) + server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) { + if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6) + assert(body.match(/test test test/)) + }) - function cb() { - server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) { - assert.equal(body.error, 'internal server error') - _cb() - }) - } - }) - }) - }) + function cb() { + server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) { + assert.equal(body.error, 'internal server error') + _cb() + }) + } + }) + }) + }) } diff --git a/test/functional/index.js b/test/functional/index.js index b618dccc3aa6..9bd3d9d6299a 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -1,80 +1,79 @@ require('./lib/startup') var assert = require('assert') - , async = require('async') - , crypto = require('crypto') - , exec = require('child_process').exec - , ex = module.exports +var async = require('async') +var exec = require('child_process').exec +var crypto = require('crypto') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } describe('Func', function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - before(function(cb) { - async.parallel([ - function(cb) { - require('./lib/startup').start('./test-storage', './config-1.yaml', cb) - }, - function(cb) { - require('./lib/startup').start('./test-storage2', './config-2.yaml', cb) - }, - ], cb) - }) + before(function(cb) { + async.parallel([ + function(cb) { + require('./lib/startup').start('./test-storage', './config-1.yaml', cb) + }, + function(cb) { + require('./lib/startup').start('./test-storage2', './config-2.yaml', cb) + }, + ], cb) + }) - before(function(cb) { - async.map([server, server2], function(server, cb) { - server.debug(function(res, body) { - server.pid = body.pid - exec('lsof -p ' + Number(server.pid), function(err, result) { - server.fdlist = result - cb() - }) - }) - }, cb) - }) + before(function(cb) { + async.map([server, server2], function(server, cb) { + server.debug(function(res, body) { + server.pid = body.pid + exec('lsof -p ' + Number(server.pid), function(err, result) { + server.fdlist = result + cb() + }) + }) + }, cb) + }) - before(function auth(cb) { - async.map([server, server2], function(server, cb) { - server.auth('test', 'test', function(res, body) { - assert.equal(res.statusCode, 201) - assert.notEqual(body.ok.indexOf('"test"'), -1) - cb() - }) - }, cb) - }) + before(function auth(cb) { + async.map([server, server2], function(server, cb) { + server.auth('test', 'test', function(res, body) { + assert.equal(res.statusCode, 201) + assert.notEqual(body.ok.indexOf('"test"'), -1) + cb() + }) + }, cb) + }) - it('authenticate', function(){/* test for before() */}) + it('authenticate', function(){/* test for before() */}) - require('./basic')() - require('./gh29')() - require('./tags')() - require('./gzip')() - require('./incomplete')() - require('./mirror')() - require('./newnpmreg')() - require('./nullstorage')() - require('./race')() - require('./racycrash')() - require('./security')() - require('./adduser')() - require('./addtag')() + require('./basic')() + require('./gh29')() + require('./tags')() + require('./gzip')() + require('./incomplete')() + require('./mirror')() + require('./newnpmreg')() + require('./nullstorage')() + require('./race')() + require('./racycrash')() + require('./security')() + require('./adduser')() + require('./addtag')() - after(function(cb) { - async.map([server, server2], function(server, cb) { - exec('lsof -p ' + Number(server.pid), function(err, result) { - assert.equal(server.fdlist, result.split('\n').filter(function(q) { - if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false; - if (q.match(/\/libcrypt-[^\/]+\.so/)) return false; - if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false; - return true; - }).join('\n')) - cb() - }) - }, cb) - }) + after(function(cb) { + async.map([server, server2], function(server, cb) { + exec('lsof -p ' + Number(server.pid), function(err, result) { + assert.equal(server.fdlist, result.split('\n').filter(function(q) { + if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false + if (q.match(/\/libcrypt-[^\/]+\.so/)) return false + if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false + return true + }).join('\n')) + cb() + }) + }, cb) + }) }) diff --git a/test/functional/lib/package.js b/test/functional/lib/package.js index 9048de9e4a64..7e904452e58c 100644 --- a/test/functional/lib/package.js +++ b/test/functional/lib/package.js @@ -1,12 +1,12 @@ module.exports = function(name, version) { - return { - "name": name, - "version": version || "0.0.0", - "dist": { - "shasum": "fake", - "tarball": "http://localhost:55551/"+escape(name)+"/-/blahblah" - } - } + return { + "name": name, + "version": version || "0.0.0", + "dist": { + "shasum": "fake", + "tarball": "http://localhost:55551/"+escape(name)+"/-/blahblah" + } + } } diff --git a/test/functional/lib/server.js b/test/functional/lib/server.js index 6e586718833b..a20911fa6909 100644 --- a/test/functional/lib/server.js +++ b/test/functional/lib/server.js @@ -1,143 +1,144 @@ +var assert = require('assert') var request = require('request') - , assert = require('assert') function Server(url) { - if (!(this instanceof Server)) return new Server(url) - this.url = url.replace(/\/$/, '') - this.userAgent = 'node/v0.10.8 linux x64' - this.authstr = 'Basic '+(new Buffer('test:test')).toString('base64') + var self = Object.create(Server.prototype) + self.url = url.replace(/\/$/, '') + self.userAgent = 'node/v0.10.8 linux x64' + self.authstr = 'Basic '+(new Buffer('test:test')).toString('base64') + return self } function prep(cb) { - return function(err, res, body) { - if (err) throw err - cb(res, body) - } + return function(err, res, body) { + if (err) throw err + cb(res, body) + } } Server.prototype.request = function(options, cb) { - assert(options.uri) - var headers = options.headers || {} - headers.accept = headers.accept || 'application/json' - headers['user-agent'] = headers['user-agent'] || this.userAgent - headers.authorization = headers.authorization || this.authstr - return request({ - url: this.url + options.uri, - method: options.method || 'GET', - headers: headers, - encoding: options.encoding, - json: options.json != null ? options.json : true, - }, cb) + assert(options.uri) + var headers = options.headers || {} + headers.accept = headers.accept || 'application/json' + headers['user-agent'] = headers['user-agent'] || this.userAgent + headers.authorization = headers.authorization || this.authstr + return request({ + url: this.url + options.uri, + method: options.method || 'GET', + headers: headers, + encoding: options.encoding, + json: options.json != null ? options.json : true, + }, cb) } Server.prototype.auth = function(user, pass, cb) { - this.authstr = 'Basic '+(new Buffer(user+':'+pass)).toString('base64') - this.request({ - uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined', - method: 'PUT', - json: { - name: user, - password: pass, - email: 'test@example.com', - _id: 'org.couchdb.user:' + user, - type: 'user', - roles: [], - date: new Date(), - } - }, prep(cb)) + this.authstr = 'Basic '+(Buffer(user+':'+pass)).toString('base64') + this.request({ + uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined', + method: 'PUT', + json: { + name: user, + password: pass, + email: 'test@example.com', + _id: 'org.couchdb.user:' + user, + type: 'user', + roles: [], + date: new Date(), + } + }, prep(cb)) } Server.prototype.get_package = function(name, cb) { - this.request({ - uri: '/'+name, - method: 'GET', - }, prep(cb)) + this.request({ + uri: '/'+name, + method: 'GET', + }, prep(cb)) } Server.prototype.put_package = function(name, data, cb) { - if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data) - this.request({ - uri: '/'+encodeURIComponent(name), - method: 'PUT', - headers: { - 'content-type': 'application/json' - }, - }, prep(cb)).end(data) + if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data) + this.request({ + uri: '/'+encodeURIComponent(name), + method: 'PUT', + headers: { + 'content-type': 'application/json' + }, + }, prep(cb)).end(data) } Server.prototype.put_version = function(name, version, data, cb) { - if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data) - this.request({ - uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest', - method: 'PUT', - headers: { - 'content-type': 'application/json' - }, - }, prep(cb)).end(data) + if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data) + this.request({ + uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest', + method: 'PUT', + headers: { + 'content-type': 'application/json' + }, + }, prep(cb)).end(data) } Server.prototype.get_tarball = function(name, filename, cb) { - this.request({ - uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename), - method: 'GET', - }, prep(cb)) + this.request({ + uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename), + method: 'GET', + }, prep(cb)) } Server.prototype.put_tarball = function(name, filename, data, cb) { - this.request({ - uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever', - method: 'PUT', - headers: { - 'content-type': 'application/octet-stream' - }, - }, prep(cb)).end(data) + this.request({ + uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever', + method: 'PUT', + headers: { + 'content-type': 'application/octet-stream' + }, + }, prep(cb)).end(data) } Server.prototype.add_tag = function(name, tag, version, cb) { - this.request({ - uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag), - method: 'PUT', - headers: { - 'content-type': 'application/json' - }, - }, prep(cb)).end(JSON.stringify(version)) + this.request({ + uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag), + method: 'PUT', + headers: { + 'content-type': 'application/json' + }, + }, prep(cb)).end(JSON.stringify(version)) } Server.prototype.put_tarball_incomplete = function(name, filename, data, size, cb) { - var req = this.request({ - uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever', - method: 'PUT', - headers: { - 'content-type': 'application/octet-stream', - 'content-length': size, - }, - timeout: 1000, - }, function(err) { - assert(err) - cb() - }) - req.write(data) - setTimeout(function() { - req.req.abort() - }, 20) + var req = this.request({ + uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever', + method: 'PUT', + headers: { + 'content-type': 'application/octet-stream', + 'content-length': size, + }, + timeout: 1000, + }, function(err) { + assert(err) + cb() + }) + req.write(data) + setTimeout(function() { + req.req.abort() + }, 20) } Server.prototype.add_package = function(name, cb) { - this.put_package(name, require('./package')(name), function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('created new package')) - cb() - }) + this.put_package(name, require('./package')(name), function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('created new package')) + cb() + }) } Server.prototype.debug = function(cb) { - this.request({ - uri: '/-/_debug', - method: 'GET', - headers: { - 'content-type': 'application/json' - }, - }, prep(cb)) + this.request({ + uri: '/-/_debug', + method: 'GET', + headers: { + 'content-type': 'application/json' + }, + }, prep(cb)) } module.exports = Server diff --git a/test/functional/lib/startup.js b/test/functional/lib/startup.js index 0e79b8a405ec..cd52849dabee 100644 --- a/test/functional/lib/startup.js +++ b/test/functional/lib/startup.js @@ -1,44 +1,43 @@ -var rimraf = require('rimraf') - , fork = require('child_process').fork - , assert = require('assert') - , express = require('express') - , readfile = require('fs').readFileSync - , Server = require('./server') +var assert = require('assert') +var fork = require('child_process').fork +var express = require('express') +var readfile = require('fs').readFileSync +var rimraf = require('rimraf') +var Server = require('./server') var forks = process.forks = [] -process.server = new Server('http://localhost:55551/') -process.server2 = new Server('http://localhost:55552/') +process.server = Server('http://localhost:55551/') +process.server2 = Server('http://localhost:55552/') process.express = express() process.express.listen(55550) module.exports.start = function start(dir, conf, cb) { - rimraf(__dirname + '/../' + dir, function() { - // filter out --debug-brk - var oldArgv = process.execArgv - process.execArgv = process.execArgv.filter(function(x) { - return x !== '--debug-brk' - }) + rimraf(__dirname + '/../' + dir, function() { + // filter out --debug-brk + var oldArgv = process.execArgv + process.execArgv = process.execArgv.filter(function(x) { + return x !== '--debug-brk' + }) - var f = fork(__dirname + '/../../../bin/sinopia' - , ['-c', __dirname + '/../' + conf] - , {silent: !process.env.TRAVIS} - ) - forks.push(f) - f.on('message', function(msg) { - if ('sinopia_started' in msg) { - cb() - cb = function(){} - } - }) - f.on('error', function(err) { - throw err - }) - process.execArgv = oldArgv - }) + var f = fork(__dirname + '/../../../bin/sinopia' + , ['-c', __dirname + '/../' + conf] + , {silent: !process.env.TRAVIS} + ) + forks.push(f) + f.on('message', function(msg) { + if ('sinopia_started' in msg) { + cb(), cb = function(){} + } + }) + f.on('error', function(err) { + throw err + }) + process.execArgv = oldArgv + }) } process.on('exit', function() { - if (forks[0]) forks[0].kill() - if (forks[1]) forks[1].kill() + if (forks[0]) forks[0].kill() + if (forks[1]) forks[1].kill() }) diff --git a/test/functional/mirror.js b/test/functional/mirror.js index 76206e9d8c02..68452ef48c69 100644 --- a/test/functional/mirror.js +++ b/test/functional/mirror.js @@ -1,75 +1,74 @@ var assert = require('assert') - , ex = module.exports function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - it('testing anti-loop', function(cb) { - server2.get_package('testloop', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) + it('testing anti-loop', function(cb) { + server2.get_package('testloop', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) - ;['fwd', /*'loop'*/].forEach(function(pkg) { - var prefix = pkg + ': ' - pkg = 'test' + pkg + ;['fwd', /*'loop'*/].forEach(function(pkg) { + var prefix = pkg + ': ' + pkg = 'test' + pkg - describe(pkg, function() { - before(function(cb) { - server.put_package(pkg, require('./lib/package')(pkg), function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('created new package')) - cb() - }) - }) + describe(pkg, function() { + before(function(cb) { + server.put_package(pkg, require('./lib/package')(pkg), function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('created new package')) + cb() + }) + }) - it(prefix+'creating new package', function(){}) + it(prefix+'creating new package', function(){}) - describe(pkg, function() { - before(function(cb) { - server.put_version(pkg, '0.1.1', require('./lib/package')(pkg), function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('published')) - cb() - }) - }) + describe(pkg, function() { + before(function(cb) { + server.put_version(pkg, '0.1.1', require('./lib/package')(pkg), function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('published')) + cb() + }) + }) - it(prefix+'uploading new package version', function(){}) + it(prefix+'uploading new package version', function(){}) - it(prefix+'uploading incomplete tarball', function(cb) { - server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000, function(res, body) { - cb() - }) - }) + it(prefix+'uploading incomplete tarball', function(cb) { + server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000, function(res, body) { + cb() + }) + }) - describe('tarball', function() { - before(function(cb) { - server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok) - cb() - }) - }) + describe('tarball', function() { + before(function(cb) { + server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok) + cb() + }) + }) - it(prefix+'uploading new tarball', function(){}) + it(prefix+'uploading new tarball', function(){}) - it(prefix+'downloading tarball from server1', function(cb) { - server.get_tarball(pkg, pkg+'.file', function(res, body) { - assert.equal(res.statusCode, 200) - assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) - cb() - }) - }) - }) - }) - }) - }) + it(prefix+'downloading tarball from server1', function(cb) { + server.get_tarball(pkg, pkg+'.file', function(res, body) { + assert.equal(res.statusCode, 200) + assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) + cb() + }) + }) + }) + }) + }) + }) } diff --git a/test/functional/newnpmreg.js b/test/functional/newnpmreg.js index 11944deb7199..125c0a1672c5 100644 --- a/test/functional/newnpmreg.js +++ b/test/functional/newnpmreg.js @@ -1,132 +1,132 @@ var assert = require('assert') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } function sha(x) { - return require('crypto').createHash('sha1', 'binary').update(x).digest('hex') + return require('crypto').createHash('sha1', 'binary').update(x).digest('hex') } module.exports = function() { - var server = process.server - var server2 = process.server2 - var express = process.express + var server = process.server + var server2 = process.server2 + var express = process.express - describe('newnpmreg', function() { - before(function(cb) { - server.request({ - uri: '/testpkg-newnpmreg', - headers: { - 'content-type': 'application/json', - }, - method: 'PUT', - json: JSON.parse(readfile('fixtures/newnpmreg.json')), - }, function(err, res, body) { - assert.equal(res.statusCode, 201) - cb() - }) - }) + describe('newnpmreg', function() { + before(function(cb) { + server.request({ + uri: '/testpkg-newnpmreg', + headers: { + 'content-type': 'application/json', + }, + method: 'PUT', + json: JSON.parse(readfile('fixtures/newnpmreg.json')), + }, function(err, res, body) { + assert.equal(res.statusCode, 201) + cb() + }) + }) - it('add pkg', function(){}) + it('add pkg', function(){}) - it('server1 - tarball', function(cb) { - server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) { - assert.equal(res.statusCode, 200) - // not real sha due to utf8 conversion - assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6') - cb() - }) - }) + it('server1 - tarball', function(cb) { + server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) { + assert.equal(res.statusCode, 200) + // not real sha due to utf8 conversion + assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6') + cb() + }) + }) - it('server2 - tarball', function(cb) { - server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) { - assert.equal(res.statusCode, 200) - // not real sha due to utf8 conversion - assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6') - cb() - }) - }) + it('server2 - tarball', function(cb) { + server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) { + assert.equal(res.statusCode, 200) + // not real sha due to utf8 conversion + assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6') + cb() + }) + }) - it('server1 - package', function(cb) { - server.get_package('testpkg-newnpmreg', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.name, 'testpkg-newnpmreg') - assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg') - assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55551/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz') - assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'}) - cb() - }) - }) + it('server1 - package', function(cb) { + server.get_package('testpkg-newnpmreg', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'testpkg-newnpmreg') + assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg') + assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55551/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz') + assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'}) + cb() + }) + }) - it('server2 - package', function(cb) { - server2.get_package('testpkg-newnpmreg', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.name, 'testpkg-newnpmreg') - assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg') - assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55552/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz') - assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'}) - cb() - }) - }) + it('server2 - package', function(cb) { + server2.get_package('testpkg-newnpmreg', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'testpkg-newnpmreg') + assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg') + assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55552/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz') + assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'}) + cb() + }) + }) - it('server1 - readme', function(cb) { - server.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body, '

blah blah blah

\n') - cb() - }) - }) + it('server1 - readme', function(cb) { + server.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body, '

blah blah blah

\n') + cb() + }) + }) - it('server2 - readme', function(cb) { - server2.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body, '

blah blah blah

\n') - cb() - }) - }) + it('server2 - readme', function(cb) { + server2.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body, '

blah blah blah

\n') + cb() + }) + }) - describe('search', function() { - function check(obj) { - obj.testpkg.time.modified = '2014-10-02T07:07:51.000Z' - assert.deepEqual(obj.testpkg, { - "name": "testpkg", - "dist-tags": { - "latest": "0.0.1" - }, - "maintainers": [], - "readmeFilename": "", - "time": { - "modified": "2014-10-02T07:07:51.000Z" - }, - "versions": { - "0.0.1": "latest" - } - }) - } + describe('search', function() { + function check(obj) { + obj.testpkg.time.modified = '2014-10-02T07:07:51.000Z' + assert.deepEqual(obj.testpkg, { + "name": "testpkg", + "dist-tags": { + "latest": "0.0.1" + }, + "maintainers": [], + "readmeFilename": "", + "time": { + "modified": "2014-10-02T07:07:51.000Z" + }, + "versions": { + "0.0.1": "latest" + } + }) + } - before(function(cb) { - express.get('/-/all', function(req, res) { - res.send({}) - }) - cb() - }) + before(function(cb) { + express.get('/-/all', function(req, res) { + res.send({}) + }) + cb() + }) - it('server1 - search', function(cb) { - server.request({uri:'/-/all'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - check(body) - cb() - }) - }) + it('server1 - search', function(cb) { + server.request({uri:'/-/all'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + check(body) + cb() + }) + }) - it('server2 - search', function(cb) { - server2.request({uri:'/-/all'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - check(body) - cb() - }) - }) - }) - }) + it('server2 - search', function(cb) { + server2.request({uri:'/-/all'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + check(body) + cb() + }) + }) + }) + }) } diff --git a/test/functional/nullstorage.js b/test/functional/nullstorage.js index 4d199cdc28ac..5a150e660bff 100644 --- a/test/functional/nullstorage.js +++ b/test/functional/nullstorage.js @@ -1,78 +1,78 @@ require('./lib/startup') var assert = require('assert') - , async = require('async') - , crypto = require('crypto') +var async = require('async') +var crypto = require('crypto') function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - it('trying to fetch non-existent package / null storage', function(cb) { - server.get_package('test-nullstorage-nonexist', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) + it('trying to fetch non-existent package / null storage', function(cb) { + server.get_package('test-nullstorage-nonexist', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) - describe('test-nullstorage on server2', function() { - before(server2.add_package.bind(server2, 'test-nullstorage2')) + describe('test-nullstorage on server2', function() { + before(server2.add_package.bind(server2, 'test-nullstorage2')) - it('creating new package - server2', function(){/* test for before() */}) + it('creating new package - server2', function(){/* test for before() */}) - it('downloading non-existent tarball', function(cb) { - server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such file')) - cb() - }) - }) + it('downloading non-existent tarball', function(cb) { + server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such file')) + cb() + }) + }) - describe('tarball', function() { - before(function(cb) { - server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(body.ok) - cb() - }) - }) + describe('tarball', function() { + before(function(cb) { + server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok) + cb() + }) + }) - before(function(cb) { - var pkg = require('./lib/package')('test-nullstorage2') - pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') - server2.put_version('test-nullstorage2', '0.0.1', pkg, function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('published')) - cb() - }) - }) + before(function(cb) { + var pkg = require('./lib/package')('test-nullstorage2') + pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') + server2.put_version('test-nullstorage2', '0.0.1', pkg, function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('published')) + cb() + }) + }) - it('uploading new tarball', function(){/* test for before() */}) + it('uploading new tarball', function(){/* test for before() */}) - it('downloading newly created tarball', function(cb) { - server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { - assert.equal(res.statusCode, 200) - assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) - cb() - }) - }) + it('downloading newly created tarball', function(cb) { + server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 200) + assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) + cb() + }) + }) - it('downloading newly created package', function(cb) { - server.get_package('test-nullstorage2', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.name, 'test-nullstorage2') - assert.equal(body.versions['0.0.1'].name, 'test-nullstorage2') - assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/test-nullstorage2/-/blahblah') - assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) - cb() - }) - }) - }) - }) + it('downloading newly created package', function(cb) { + server.get_package('test-nullstorage2', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'test-nullstorage2') + assert.equal(body.versions['0.0.1'].name, 'test-nullstorage2') + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/test-nullstorage2/-/blahblah') + assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) + cb() + }) + }) + }) + }) } diff --git a/test/functional/race.js b/test/functional/race.js index 8e444104c0d6..6e61d6375a92 100644 --- a/test/functional/race.js +++ b/test/functional/race.js @@ -1,94 +1,94 @@ -var assert = require('assert') - , readfile = require('fs').readFileSync - , ex = module.exports - , async = require('async') - , _oksum = 0 +var assert = require('assert') +var async = require('async') +var readfile = require('fs').readFileSync +var ex = module.exports +var _oksum = 0 module.exports = function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - describe('race', function() { - before(function(cb) { - server.put_package('race', require('./lib/package')('race'), function(res, body) { - assert.equal(res.statusCode, 201) - assert(~body.ok.indexOf('created new package')) - cb() - }) - }) + describe('race', function() { + before(function(cb) { + server.put_package('race', require('./lib/package')('race'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('created new package')) + cb() + }) + }) - it('creating new package', function(){}) + it('creating new package', function(){}) - it('uploading 10 same versions', function(cb) { - var fns = [] - for (var i=0; i<10; i++) { - fns.push(function(cb_) { - var data = require('./lib/package')('race') - data.rand = Math.random() - server.put_version('race', '0.0.1', data, function(res, body) { - cb_(null, res, body) - }) - }) - } + it('uploading 10 same versions', function(cb) { + var fns = [] + for (var i=0; i<10; i++) { + fns.push(function(cb_) { + var data = require('./lib/package')('race') + data.rand = Math.random() + server.put_version('race', '0.0.1', data, function(res, body) { + cb_(null, res, body) + }) + }) + } - async.parallel(fns, function(err, res) { - var okcount = 0 - , failcount = 0 + async.parallel(fns, function(err, res) { + var okcount = 0 + , failcount = 0 - res.forEach(function(arr) { - var resp = arr[0] - , body = arr[1] + res.forEach(function(arr) { + var resp = arr[0] + , body = arr[1] - if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ - if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ - if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++ - }) - assert.equal(okcount + failcount, 10) - assert.equal(okcount, 1) - _oksum += okcount + if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ + if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ + if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++ + }) + assert.equal(okcount + failcount, 10) + assert.equal(okcount, 1) + _oksum += okcount - cb() - }) - }) + cb() + }) + }) - it('uploading 10 diff versions', function(cb) { - var fns = [] - for (var i=0; i<10; i++) { - ;(function(i) { - fns.push(function(cb_) { - server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'), function(res, body) { - cb_(null, res, body) - }) - }) - })(i) - } + it('uploading 10 diff versions', function(cb) { + var fns = [] + for (var i=0; i<10; i++) { + ;(function(i) { + fns.push(function(cb_) { + server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'), function(res, body) { + cb_(null, res, body) + }) + }) + })(i) + } - async.parallel(fns, function(err, res) { - var okcount = 0 - , failcount = 0 + async.parallel(fns, function(err, res) { + var okcount = 0 + , failcount = 0 - res.forEach(function(arr) { - var resp = arr[0] - , body = arr[1] - if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ - if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ - if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++ - }) - assert.equal(okcount + failcount, 10) - _oksum += okcount + res.forEach(function(arr) { + var resp = arr[0] + , body = arr[1] + if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ + if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ + if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++ + }) + assert.equal(okcount + failcount, 10) + _oksum += okcount - cb() - }) - }) + cb() + }) + }) - // XXX: this should be after anything else, but we can't really ensure that with mocha - it('downloading package', function(cb) { - server.get_package('race', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(Object.keys(body.versions).length, _oksum) - cb() - }) - }) - }) + // XXX: this should be after anything else, but we can't really ensure that with mocha + it('downloading package', function(cb) { + server.get_package('race', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(Object.keys(body.versions).length, _oksum) + cb() + }) + }) + }) } diff --git a/test/functional/racycrash.js b/test/functional/racycrash.js index d803be5c4e9c..206eec58c7d5 100644 --- a/test/functional/racycrash.js +++ b/test/functional/racycrash.js @@ -1,69 +1,68 @@ var assert = require('assert') - , ex = module.exports module.exports = function() { - var server = process.server - var express = process.express + var server = process.server + var express = process.express - describe('Racy', function() { - var on_tarball + describe('Racy', function() { + var on_tarball - before(function() { - express.get('/testexp-racycrash', function(_, res) { - res.send({ - "name": "testexp-racycrash", - "versions": { - "0.1.0": { - "name": "testexp_tags", - "version": "0.1.0", - "dist": { - "shasum": "fake", - "tarball": "http://localhost:55550/testexp-racycrash/-/test.tar.gz" - } - } - } - }) - }) + before(function() { + express.get('/testexp-racycrash', function(_, res) { + res.send({ + "name": "testexp-racycrash", + "versions": { + "0.1.0": { + "name": "testexp_tags", + "version": "0.1.0", + "dist": { + "shasum": "fake", + "tarball": "http://localhost:55550/testexp-racycrash/-/test.tar.gz" + } + } + } + }) + }) - express.get('/testexp-racycrash/-/test.tar.gz', function(_, res) { - on_tarball(res) - }) - }) + express.get('/testexp-racycrash/-/test.tar.gz', function(_, res) { + on_tarball(res) + }) + }) - it('should not crash on error if client disconnects', function(_cb) { - on_tarball = function(res) { - res.header('content-length', 1e6) - res.write('test test test\n') - setTimeout(function() { - res.write('test test test\n') - res.socket.destroy() - cb() - }, 200) - } + it('should not crash on error if client disconnects', function(_cb) { + on_tarball = function(res) { + res.header('content-length', 1e6) + res.write('test test test\n') + setTimeout(function() { + res.write('test test test\n') + res.socket.destroy() + cb() + }, 200) + } - server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { - assert.equal(body, 'test test test\n') - }) + server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { + assert.equal(body, 'test test test\n') + }) - function cb() { - // test for NOT crashing - server.request({uri:'/testexp-racycrash'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - _cb() - }) - } - }) + function cb() { + // test for NOT crashing + server.request({uri:'/testexp-racycrash'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + _cb() + }) + } + }) - it('should not store tarball', function(cb) { - on_tarball = function(res) { - res.socket.destroy() - } + it('should not store tarball', function(cb) { + on_tarball = function(res) { + res.socket.destroy() + } - server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { - assert.equal(body.error, 'internal server error') - cb() - }) - }) - }) + server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { + assert.equal(body.error, 'internal server error') + cb() + }) + }) + }) } diff --git a/test/functional/security.js b/test/functional/security.js index 1f318cc5a4c6..bcc686ef08d1 100644 --- a/test/functional/security.js +++ b/test/functional/security.js @@ -1,88 +1,87 @@ var assert = require('assert') - , ex = module.exports module.exports = function() { - var server = process.server - var server2 = process.server2 + var server = process.server + var server2 = process.server2 - describe('Security', function() { - before(server.add_package.bind(server, 'testpkg-sec')) + describe('Security', function() { + before(server.add_package.bind(server, 'testpkg-sec')) - it('bad pkg #1', function(cb) { - server.get_package('package.json', function(res, body) { - assert.equal(res.statusCode, 403) - assert(~body.error.indexOf('invalid package')) - cb() - }) - }) + it('bad pkg #1', function(cb) { + server.get_package('package.json', function(res, body) { + assert.equal(res.statusCode, 403) + assert(~body.error.indexOf('invalid package')) + cb() + }) + }) - it('bad pkg #2', function(cb) { - server.get_package('__proto__', function(res, body) { - assert.equal(res.statusCode, 403) - assert(~body.error.indexOf('invalid package')) - cb() - }) - }) + it('bad pkg #2', function(cb) { + server.get_package('__proto__', function(res, body) { + assert.equal(res.statusCode, 403) + assert(~body.error.indexOf('invalid package')) + cb() + }) + }) - it('__proto__, connect stuff', function(cb) { - server.request({uri:'/testpkg-sec?__proto__=1'}, function(err, res, body) { - // test for NOT outputting stack trace - assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1) + it('__proto__, connect stuff', function(cb) { + server.request({uri:'/testpkg-sec?__proto__=1'}, function(err, res, body) { + // test for NOT outputting stack trace + assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1) - // test for NOT crashing - server.request({uri:'/testpkg-sec'}, function(err, res, body) { - assert.equal(res.statusCode, 200) - cb() - }) - }) - }) + // test for NOT crashing + server.request({uri:'/testpkg-sec'}, function(err, res, body) { + assert.equal(res.statusCode, 200) + cb() + }) + }) + }) - it('do not return package.json as an attachment', function(cb) { - server.request({uri:'/testpkg-sec/-/package.json'}, function(err, res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/invalid filename/)) - cb() - }) - }) + it('do not return package.json as an attachment', function(cb) { + server.request({uri:'/testpkg-sec/-/package.json'}, function(err, res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/invalid filename/)) + cb() + }) + }) - it('silly things - reading #1', function(cb) { - server.request({uri:'/testpkg-sec/-/../../../../../../../../etc/passwd'}, function(err, res, body) { - assert.equal(res.statusCode, 404) - cb() - }) - }) + it('silly things - reading #1', function(cb) { + server.request({uri:'/testpkg-sec/-/../../../../../../../../etc/passwd'}, function(err, res, body) { + assert.equal(res.statusCode, 404) + cb() + }) + }) - it('silly things - reading #2', function(cb) { - server.request({uri:'/testpkg-sec/-/%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'}, function(err, res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/invalid filename/)) - cb() - }) - }) + it('silly things - reading #2', function(cb) { + server.request({uri:'/testpkg-sec/-/%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'}, function(err, res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/invalid filename/)) + cb() + }) + }) - it('silly things - writing #1', function(cb) { - server.put_tarball('testpkg-sec', 'package.json', '{}', function(res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/invalid filename/)) - cb() - }) - }) + it('silly things - writing #1', function(cb) { + server.put_tarball('testpkg-sec', 'package.json', '{}', function(res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/invalid filename/)) + cb() + }) + }) - it('silly things - writing #3', function(cb) { - server.put_tarball('testpkg-sec', 'node_modules', '{}', function(res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/invalid filename/)) - cb() - }) - }) + it('silly things - writing #3', function(cb) { + server.put_tarball('testpkg-sec', 'node_modules', '{}', function(res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/invalid filename/)) + cb() + }) + }) - it('silly things - writing #4', function(cb) { - server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}', function(res, body) { - assert.equal(res.statusCode, 403) - assert(body.error.match(/invalid filename/)) - cb() - }) - }) - }) + it('silly things - writing #4', function(cb) { + server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}', function(res, body) { + assert.equal(res.statusCode, 403) + assert(body.error.match(/invalid filename/)) + cb() + }) + }) + }) } diff --git a/test/functional/tags.js b/test/functional/tags.js index c985a3253540..b71f3c92a498 100644 --- a/test/functional/tags.js +++ b/test/functional/tags.js @@ -1,51 +1,50 @@ var assert = require('assert') - , ex = module.exports function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) + return require('fs').readFileSync(__dirname + '/' + x) } module.exports = function() { - var server = process.server - var express = process.express + var server = process.server + var express = process.express - it('tags - testing for 404', function(cb) { - server.get_package('testexp_tags', function(res, body) { - // shouldn't exist yet - assert.equal(res.statusCode, 404) - assert(~body.error.indexOf('no such package')) - cb() - }) - }) + it('tags - testing for 404', function(cb) { + server.get_package('testexp_tags', function(res, body) { + // shouldn't exist yet + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) - describe('tags', function() { - before(function(cb) { - express.get('/testexp_tags', function(req, res) { - res.send(JSON.parse(readfile('fixtures/tags.json'))) - }) - cb() - }) + describe('tags', function() { + before(function(cb) { + express.get('/testexp_tags', function(req, res) { + res.send(JSON.parse(readfile('fixtures/tags.json'))) + }) + cb() + }) - it('fetching package again', function(cb) { - server.get_package('testexp_tags', function(res, body) { - assert.equal(res.statusCode, 200) - assert.equal(typeof(body.versions['1.1']), 'object') - assert.equal(body['dist-tags'].something, '0.1.1alpha') - // note: 5.4.3 is invalid tag, 0.1.3alpha is highest semver - assert.equal(body['dist-tags'].latest, '0.1.3alpha') - assert.equal(body['dist-tags'].bad, null) - cb() - }) - }) + it('fetching package again', function(cb) { + server.get_package('testexp_tags', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(typeof(body.versions['1.1']), 'object') + assert.equal(body['dist-tags'].something, '0.1.1alpha') + // note: 5.4.3 is invalid tag, 0.1.3alpha is highest semver + assert.equal(body['dist-tags'].latest, '0.1.3alpha') + assert.equal(body['dist-tags'].bad, null) + cb() + }) + }) - ;['0.1.1alpha', '0.1.1-alpha', '0000.00001.001-alpha'].forEach(function(ver) { - it('fetching '+ver, function(cb) { - server.request({uri:'/testexp_tags/'+ver}, function(err, res, body) { - assert.equal(res.statusCode, 200) - assert.equal(body.version, '0.1.1alpha') - cb() - }) - }) - }) - }) + ;['0.1.1alpha', '0.1.1-alpha', '0000.00001.001-alpha'].forEach(function(ver) { + it('fetching '+ver, function(cb) { + server.request({uri:'/testexp_tags/'+ver}, function(err, res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.version, '0.1.1alpha') + cb() + }) + }) + }) + }) } diff --git a/test/unit/config_def.js b/test/unit/config_def.js index 907a87c37fe6..34b69e9892f8 100644 --- a/test/unit/config_def.js +++ b/test/unit/config_def.js @@ -1,8 +1,8 @@ describe('config.yaml', function() { - it('should be parseable', function() { - var source = require('fs').readFileSync(__dirname + '/../../lib/config_def.yaml', 'utf8') - require('js-yaml').safeLoad(source) - }) + it('should be parseable', function() { + var source = require('fs').readFileSync(__dirname + '/../../lib/config_def.yaml', 'utf8') + require('js-yaml').safeLoad(source) + }) }) diff --git a/test/unit/mystreams.js b/test/unit/mystreams.js index 7105d894f1b2..de6894e4b762 100644 --- a/test/unit/mystreams.js +++ b/test/unit/mystreams.js @@ -1,17 +1,17 @@ var ReadTarball = require('../../lib/streams').ReadTarballStream describe('mystreams', function() { - it('should delay events', function(cb) { - var test = new ReadTarball() - test.abort() - setTimeout(function() { - test.abort = function() { - cb() - } - test.abort = function() { - throw new Error('fail') - } - }, 10) - }) + it('should delay events', function(cb) { + var test = new ReadTarball() + test.abort() + setTimeout(function() { + test.abort = function() { + cb() + } + test.abort = function() { + throw Error('fail') + } + }, 10) + }) }) diff --git a/test/unit/no_proxy.js b/test/unit/no_proxy.js index 2590da08940e..3176675156f1 100644 --- a/test/unit/no_proxy.js +++ b/test/unit/no_proxy.js @@ -1,88 +1,88 @@ -var assert = require('assert') - , Storage = require('../../lib/up-storage') +var assert = require('assert') +var Storage = require('../../lib/up-storage') require('../../lib/logger').setup([]) function setup(host, config, mainconfig) { - config.url = host - return new Storage(config, mainconfig) + config.url = host + return Storage(config, mainconfig) } describe('Use proxy', function() { - it('should work fine without proxy', function() { - var x = setup('http://x/x', {}, {}) - assert.equal(x.proxy, null) - }) + it('should work fine without proxy', function() { + var x = setup('http://x/x', {}, {}) + assert.equal(x.proxy, null) + }) - it('local config should take priority', function() { - var x = setup('http://x/x', {http_proxy: '123'}, {http_proxy: '456'}) - assert.equal(x.proxy, '123') - }) + it('local config should take priority', function() { + var x = setup('http://x/x', {http_proxy: '123'}, {http_proxy: '456'}) + assert.equal(x.proxy, '123') + }) - it('no_proxy is invalid', function() { - var x = setup('http://x/x', {http_proxy: '123', no_proxy: false}, {}) - assert.equal(x.proxy, '123') - var x = setup('http://x/x', {http_proxy: '123', no_proxy: null}, {}) - assert.equal(x.proxy, '123') - var x = setup('http://x/x', {http_proxy: '123', no_proxy: []}, {}) - assert.equal(x.proxy, '123') - var x = setup('http://x/x', {http_proxy: '123', no_proxy: ''}, {}) - assert.equal(x.proxy, '123') - }) + it('no_proxy is invalid', function() { + var x = setup('http://x/x', {http_proxy: '123', no_proxy: false}, {}) + assert.equal(x.proxy, '123') + var x = setup('http://x/x', {http_proxy: '123', no_proxy: null}, {}) + assert.equal(x.proxy, '123') + var x = setup('http://x/x', {http_proxy: '123', no_proxy: []}, {}) + assert.equal(x.proxy, '123') + var x = setup('http://x/x', {http_proxy: '123', no_proxy: ''}, {}) + assert.equal(x.proxy, '123') + }) - it('no_proxy - simple/include', function() { - var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'}) - assert.equal(x.proxy, undefined) - }) + it('no_proxy - simple/include', function() { + var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'}) + assert.equal(x.proxy, undefined) + }) - it('no_proxy - simple/not', function() { - var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'}) - assert.equal(x.proxy, '123') - }) + it('no_proxy - simple/not', function() { + var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'}) + assert.equal(x.proxy, '123') + }) - it('no_proxy - various, single string', function() { - var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'}) - assert.equal(x.proxy, '123') - var x = setup('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'}) - assert.equal(x.proxy, null) - var x = setup('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'}) - assert.equal(x.proxy, '123') - var x = setup('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {}) - assert.equal(x.proxy, null) - var x = setup('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {}) - assert.equal(x.proxy, null) - var x = setup('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {}) - assert.equal(x.proxy, '123') - }) + it('no_proxy - various, single string', function() { + var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'}) + assert.equal(x.proxy, '123') + var x = setup('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'}) + assert.equal(x.proxy, null) + var x = setup('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'}) + assert.equal(x.proxy, '123') + var x = setup('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {}) + assert.equal(x.proxy, null) + var x = setup('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {}) + assert.equal(x.proxy, null) + var x = setup('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {}) + assert.equal(x.proxy, '123') + }) - it('no_proxy - various, array', function() { - var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) - assert.equal(x.proxy, '123') - var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) - assert.equal(x.proxy, null) - var x = setup('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) - assert.equal(x.proxy, null) - var x = setup('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) - assert.equal(x.proxy, '123') - var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']}) - assert.equal(x.proxy, '123') - var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']}) - assert.equal(x.proxy, null) - }) + it('no_proxy - various, array', function() { + var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) + assert.equal(x.proxy, '123') + var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) + assert.equal(x.proxy, null) + var x = setup('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) + assert.equal(x.proxy, null) + var x = setup('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'}) + assert.equal(x.proxy, '123') + var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']}) + assert.equal(x.proxy, '123') + var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']}) + assert.equal(x.proxy, null) + }) - it('no_proxy - hostport', function() { - var x = setup('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'}) - assert.equal(x.proxy, null) - var x = setup('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'}) - assert.equal(x.proxy, null) - }) + it('no_proxy - hostport', function() { + var x = setup('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'}) + assert.equal(x.proxy, null) + var x = setup('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'}) + assert.equal(x.proxy, null) + }) - it('no_proxy - secure', function() { - var x = setup('https://something', {http_proxy: '123'}, {}) - assert.equal(x.proxy, null) - var x = setup('https://something', {https_proxy: '123'}, {}) - assert.equal(x.proxy, '123') - var x = setup('https://something', {http_proxy: '456', https_proxy: '123'}, {}) - assert.equal(x.proxy, '123') - }) + it('no_proxy - secure', function() { + var x = setup('https://something', {http_proxy: '123'}, {}) + assert.equal(x.proxy, null) + var x = setup('https://something', {https_proxy: '123'}, {}) + assert.equal(x.proxy, '123') + var x = setup('https://something', {http_proxy: '456', https_proxy: '123'}, {}) + assert.equal(x.proxy, '123') + }) }) diff --git a/test/unit/parse_interval.js b/test/unit/parse_interval.js index 4b35a294241c..f1e9ac643ea6 100644 --- a/test/unit/parse_interval.js +++ b/test/unit/parse_interval.js @@ -1,34 +1,34 @@ -var assert = require('assert') - , parse_interval = require('../../lib/config').parse_interval +var assert = require('assert') +var parse_interval = require('../../lib/config').parse_interval describe('Parse interval', function() { - function add_test(str, res) { - it('parse ' + str, function() { - if (res === null) { - assert.throws(function() { - console.log(parse_interval(str)) - }) - } else { - assert.strictEqual(parse_interval(str), res) - } - }) - } + function add_test(str, res) { + it('parse ' + str, function() { + if (res === null) { + assert.throws(function() { + console.log(parse_interval(str)) + }) + } else { + assert.strictEqual(parse_interval(str), res) + } + }) + } - add_test(12345, 12345000) - add_test('1000', 1000000) - add_test('1.5s', 1500) - add_test('25ms', 25) - add_test('2m', 2*1000*60) - add_test('3h', 3*1000*60*60) - add_test('0.5d', 0.5*1000*60*60*24) - add_test('0.5w', 0.5*1000*60*60*24*7) - add_test('1M', 1000*60*60*24*30) - add_test('5s 20ms', 5020) - add_test('1y', 1000*60*60*24*365) - add_test('1y 5', null) - add_test('1m 1m', null) - add_test('1m 1y', null) - add_test('1y 1M 1w 1d 1h 1m 1s 1ms', 34822861001) - add_test(' 5s 25ms ', 5025) + add_test(12345, 12345000) + add_test('1000', 1000000) + add_test('1.5s', 1500) + add_test('25ms', 25) + add_test('2m', 2*1000*60) + add_test('3h', 3*1000*60*60) + add_test('0.5d', 0.5*1000*60*60*24) + add_test('0.5w', 0.5*1000*60*60*24*7) + add_test('1M', 1000*60*60*24*30) + add_test('5s 20ms', 5020) + add_test('1y', 1000*60*60*24*365) + add_test('1y 5', null) + add_test('1m 1m', null) + add_test('1m 1y', null) + add_test('1y 1M 1w 1d 1h 1m 1s 1ms', 34822861001) + add_test(' 5s 25ms ', 5025) }) diff --git a/test/unit/st_merge.js b/test/unit/st_merge.js index 9e4d424abc37..d07988fd16a1 100644 --- a/test/unit/st_merge.js +++ b/test/unit/st_merge.js @@ -1,53 +1,53 @@ -var assert = require('assert') - , semver_sort = require('../../lib/utils').semver_sort - , merge = require('../../lib/storage')._merge_versions +var assert = require('assert') +var semver_sort = require('../../lib/utils').semver_sort +var merge = require('../../lib/storage')._merge_versions require('../../lib/logger').setup([]) describe('Merge', function() { - it('simple', function() { - var x = { - versions: {a:1,b:1,c:1}, - 'dist-tags': {}, - } - merge(x, {versions: {a:2,q:2}}) - assert.deepEqual(x, { - versions: {a:1,b:1,c:1,q:2}, - 'dist-tags': {}, - }) - }) + it('simple', function() { + var x = { + versions: {a:1,b:1,c:1}, + 'dist-tags': {}, + } + merge(x, {versions: {a:2,q:2}}) + assert.deepEqual(x, { + versions: {a:1,b:1,c:1,q:2}, + 'dist-tags': {}, + }) + }) - it('dist-tags - compat', function() { - var x = { - versions: {}, - 'dist-tags': {q:'1.1.1',w:['2.2.2']}, - } - merge(x, {'dist-tags':{q:'2.2.2',w:'3.3.3',t:'4.4.4'}}) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {q:['1.1.1','2.2.2'],w:['2.2.2','3.3.3'],t:['4.4.4']}, - }) - }) + it('dist-tags - compat', function() { + var x = { + versions: {}, + 'dist-tags': {q:'1.1.1',w:['2.2.2']}, + } + merge(x, {'dist-tags':{q:'2.2.2',w:'3.3.3',t:'4.4.4'}}) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {q:['1.1.1','2.2.2'],w:['2.2.2','3.3.3'],t:['4.4.4']}, + }) + }) - it('dist-tags - sort', function() { - var x = { - versions: {}, - 'dist-tags': {w:['2.2.2','1.1.1','12.2.2','2.2.2-rc2']}, - } - merge(x, {'dist-tags':{w:'3.3.3'}}) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {w:["1.1.1","2.2.2-rc2","2.2.2","3.3.3","12.2.2"]}, - }) - }) + it('dist-tags - sort', function() { + var x = { + versions: {}, + 'dist-tags': {w:['2.2.2','1.1.1','12.2.2','2.2.2-rc2']}, + } + merge(x, {'dist-tags':{w:'3.3.3'}}) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {w:["1.1.1","2.2.2-rc2","2.2.2","3.3.3","12.2.2"]}, + }) + }) - it('semver_sort', function() { - assert.deepEqual(semver_sort(['1.2.3','1.2','1.2.3a','1.2.3c','1.2.3-b']), - [ '1.2.3a', - '1.2.3-b', - '1.2.3c', - '1.2.3' ] - ) - }) + it('semver_sort', function() { + assert.deepEqual(semver_sort(['1.2.3','1.2','1.2.3a','1.2.3c','1.2.3-b']), + [ '1.2.3a', + '1.2.3-b', + '1.2.3c', + '1.2.3' ] + ) + }) }) diff --git a/test/unit/tag_version.js b/test/unit/tag_version.js index 9e3b8075e300..b8f2f1c465e1 100644 --- a/test/unit/tag_version.js +++ b/test/unit/tag_version.js @@ -1,55 +1,55 @@ -var assert = require('assert') - , tag_version = require('../../lib/utils').tag_version +var assert = require('assert') +var tag_version = require('../../lib/utils').tag_version require('../../lib/logger').setup([]) describe('tag_version', function() { - it('add new one', function() { - var x = { - versions: {}, - 'dist-tags': {}, - } - assert(tag_version(x, '1.1.1', 'foo', {})) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {foo: ['1.1.1']}, - }) - }) + it('add new one', function() { + var x = { + versions: {}, + 'dist-tags': {}, + } + assert(tag_version(x, '1.1.1', 'foo', {})) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {foo: ['1.1.1']}, + }) + }) - it('add (compat)', function() { - var x = { - versions: {}, - 'dist-tags': {foo: '1.1.0'}, - } - assert(tag_version(x, '1.1.1', 'foo', {})) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {foo: ['1.1.0', '1.1.1']}, - }) - }) + it('add (compat)', function() { + var x = { + versions: {}, + 'dist-tags': {foo: '1.1.0'}, + } + assert(tag_version(x, '1.1.1', 'foo', {})) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {foo: ['1.1.0', '1.1.1']}, + }) + }) - it('add fresh tag', function() { - var x = { - versions: {}, - 'dist-tags': {foo: ['1.1.0']}, - } - assert(tag_version(x, '1.1.1', 'foo', {})) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {foo: ['1.1.0', '1.1.1']}, - }) - }) + it('add fresh tag', function() { + var x = { + versions: {}, + 'dist-tags': {foo: ['1.1.0']}, + } + assert(tag_version(x, '1.1.1', 'foo', {})) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {foo: ['1.1.0', '1.1.1']}, + }) + }) - it('add stale tag', function() { - var x = { - versions: {}, - 'dist-tags': {foo: ['1.1.2']}, - } - assert(!tag_version(x, '1.1.1', 'foo', {})) - assert.deepEqual(x, { - versions: {}, - 'dist-tags': {foo: ['1.1.1', '1.1.2']}, - }) - }) + it('add stale tag', function() { + var x = { + versions: {}, + 'dist-tags': {foo: ['1.1.2']}, + } + assert(!tag_version(x, '1.1.1', 'foo', {})) + assert.deepEqual(x, { + versions: {}, + 'dist-tags': {foo: ['1.1.1', '1.1.2']}, + }) + }) }) diff --git a/test/unit/utils.js b/test/unit/utils.js index bf407d06d49e..a5f3c4148d08 100644 --- a/test/unit/utils.js +++ b/test/unit/utils.js @@ -1,42 +1,42 @@ -var assert = require('assert') - , validate = require('../../lib/utils').validate_name +var assert = require('assert') +var validate = require('../../lib/utils').validate_name describe('Validate', function() { - it('good ones', function() { - assert(validate('sinopia')) - assert(validate('some.weird.package-zzz')) - assert(validate('old-package@0.1.2.tgz')) - }) + it('good ones', function() { + assert( validate('sinopia') ) + assert( validate('some.weird.package-zzz') ) + assert( validate('old-package@0.1.2.tgz') ) + }) - it('uppercase', function() { - assert(validate('EVE')) - assert(validate('JSONStream')) - }) + it('uppercase', function() { + assert( validate('EVE') ) + assert( validate('JSONStream') ) + }) - it('no package.json', function() { - assert(!validate('package.json')) - }) + it('no package.json', function() { + assert( !validate('package.json') ) + }) - it('no path seps', function() { - assert(!validate('some/thing')) - assert(!validate('some\\thing')) - }) - - it('no hidden', function() { - assert(!validate('.bin')) - }) - - it('no reserved', function() { - assert(!validate('favicon.ico')) - assert(!validate('node_modules')) - assert(!validate('__proto__')) - }) + it('no path seps', function() { + assert( !validate('some/thing') ) + assert( !validate('some\\thing') ) + }) + + it('no hidden', function() { + assert( !validate('.bin') ) + }) + + it('no reserved', function() { + assert( !validate('favicon.ico') ) + assert( !validate('node_modules') ) + assert( !validate('__proto__') ) + }) - it('other', function() { - assert(!validate('pk g')) - assert(!validate('pk\tg')) - assert(!validate('pk%20g')) - assert(!validate('pk+g')) - assert(!validate('pk:g')) - }) + it('other', function() { + assert( !validate('pk g') ) + assert( !validate('pk\tg') ) + assert( !validate('pk%20g') ) + assert( !validate('pk+g') ) + assert( !validate('pk:g') ) + }) }) diff --git a/test/unit/validate_all.js b/test/unit/validate_all.js index a986acc72745..09a03c0b9df8 100644 --- a/test/unit/validate_all.js +++ b/test/unit/validate_all.js @@ -3,31 +3,31 @@ var assert = require('assert') describe('index.js app', function() { - var source = require('fs').readFileSync(__dirname + '/../../lib/index.js', 'utf8') - - var very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g - var m - var params = {} - - while ((m = very_scary_regexp.exec(source)) != null) { - if (m[1] === 'set') continue - - var inner = m[2].slice(1, m[2].length-1) - var t - - inner.split('/').forEach(function(x) { - if (m[1] === 'param') { - params[x] = 'ok' - } else if (t = x.match(/^:([^?:]*)\??$/)) { - params[t[1]] = params[t[1]] || m[0].trim() - } - }) - } - - Object.keys(params).forEach(function(param) { - it('should validate ":'+param+'"', function() { - assert.equal(params[param], 'ok') - }) - }) + var source = require('fs').readFileSync(__dirname + '/../../lib/index.js', 'utf8') + + var very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g + var m + var params = {} + + while ((m = very_scary_regexp.exec(source)) != null) { + if (m[1] === 'set') continue + + var inner = m[2].slice(1, m[2].length-1) + var t + + inner.split('/').forEach(function(x) { + if (m[1] === 'param') { + params[x] = 'ok' + } else if (t = x.match(/^:([^?:]*)\??$/)) { + params[t[1]] = params[t[1]] || m[0].trim() + } + }) + } + + Object.keys(params).forEach(function(param) { + it('should validate ":'+param+'"', function() { + assert.equal(params[param], 'ok') + }) + }) })