Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server): Add support for encoded source files #3123

Merged
merged 1 commit into from Sep 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/dev/05-plugins.md
Expand Up @@ -18,6 +18,17 @@ Karma can be extended through plugins. A plugin is essentially an NPM module. Ty
- use NPM keywords `karma-plugin`, `karma-launcher`

## Preprocessors

A preprocessor is a function that accepts three arguments (`content`, `file`, and `next`), mutates the content in some way, and passes it on to the next preprocessor.

- arguments passed to preprocessor plugins:
- **`content`** of the file being processed
- **`file`** object describing the file being processed
- **path:** the current file, mutable file path. e. g. `some/file.coffee` -> `some/file.coffee.js` _This path is mutable and may not actually exist._
- **originalPath:** the original, unmutated path
- **encodings:** A mutable, keyed object where the keys are a valid encoding type ('gzip', 'compress', 'br', etc.) and the values are the encoded content. Encoded content should be stored here and not resolved using `next(null, encodedContent)`
- **type:** the pattern used to match the file
- **`next`** function to be called when preprocessing is complete, should be called as `next(null, processedContent)` or `next(error)`
- example plugins: [karma-coffee-preprocessor], [karma-ng-html2js-preprocessor]
- use naming convention is `karma-*-preprocessor`
- user NPM keywords `karma-plugin`, `karma-preprocessor`
Expand Down
4 changes: 4 additions & 0 deletions lib/file.js
Expand Up @@ -14,6 +14,10 @@ class File {
// where the content is stored (processed)
this.contentPath = path

// encodings format {[encodingType]: encodedContent}
// example: {gzip: <Buffer 1f 8b 08...>}
GreenGremlin marked this conversation as resolved.
Show resolved Hide resolved
this.encodings = Object.create(null)

this.mtime = mtime
this.isUrl = false

Expand Down
11 changes: 10 additions & 1 deletion lib/middleware/source_files.js
Expand Up @@ -35,13 +35,22 @@ function createSourceFilesMiddleware (filesPromise, serveFile, basePath, urlRoot
const rangeHeader = request.headers['range']

if (file) {
const acceptEncodingHeader = request.headers['accept-encoding']
const matchedEncoding = Object.keys(file.encodings).find(
(encoding) => new RegExp(`(^|.*, ?)${encoding}(,|$)`).test(acceptEncodingHeader)
)
const content = file.encodings[matchedEncoding] || file.content

serveFile(file.contentPath || file.path, rangeHeader, response, function () {
if (/\?\w+/.test(request.url)) {
common.setHeavyCacheHeaders(response) // files with timestamps - cache one year, rely on timestamps
} else {
common.setNoCacheHeaders(response) // without timestamps - no cache (debug)
}
}, file.content, file.doNotCache)
if (matchedEncoding) {
response.setHeader('Content-Encoding', matchedEncoding)
}
}, content, file.doNotCache)
} else {
next()
}
Expand Down
36 changes: 36 additions & 0 deletions test/unit/middleware/source_files.spec.js
@@ -1,6 +1,7 @@
var http = require('http')
var mocks = require('mocks')
var request = require('supertest')
var zlib = require('zlib')

var helper = require('../../../lib/helper')
var File = require('../../../lib/file')
Expand Down Expand Up @@ -109,6 +110,41 @@ describe('middleware.source_files', function () {
})
})

describe('file encoding', function () {
let file
beforeEach(function () {
file = new File('/src/some.js')
servedFiles([
file
])
})

it('serves encoded files', function () {
file.encodings.gzip = zlib.gzipSync('gzipped-js-source')
return request(server)
.get('/absolute/src/some.js')
.set('Accept-Encoding', 'gzip, deflate')
.expect(200, 'gzipped-js-source')
.expect('Content-Encoding', 'gzip')
.expect('Content-Type', 'application/javascript')
})

it('serves unencoded files when request does not accept available encodings', function (done) {
file.encodings.gzip = zlib.gzipSync('gzipped-js-source')
request(server)
.get('/absolute/src/some.js')
.set('Accept-Encoding', 'gzippy, deflate')
.expect(200, 'js-source')
.end((error, res) => {
if (error) {
return done(error)
}
expect(res.headers).to.not.have.property('content-encoding')
return done()
})
})
})

it('should serve absolute js source files ignoring timestamp', function () {
servedFiles([
new File('/src/some.js')
Expand Down