Comparing version 0.3.0 to 0.3.1
201
index.js
@@ -1,5 +0,5 @@ | ||
"use strict"; | ||
'use strict'; | ||
const P = require('bluebird'); | ||
const dgram = P.promisifyAll(require('dgram')); | ||
const P = require( 'bluebird' ); | ||
const dgram = P.promisifyAll( require( 'dgram' ) ); | ||
@@ -9,3 +9,3 @@ /** | ||
* | ||
* @param options object containing options for a cache purger: | ||
* @param {Object} options object containing options for a cache purger: | ||
* - log: logging function (default no-op) | ||
@@ -20,34 +20,34 @@ * - routes: array of route objects to map a resource url to the cache endpoint | ||
class HTCPPurger { | ||
constructor(options) { | ||
this.options = options || {}; | ||
this.log = this.options.log || (() => {}); | ||
constructor( options ) { | ||
this.options = options || {}; | ||
this.log = this.options.log || ( () => {} ); | ||
if (!this.options.routes) { | ||
throw new Error('Config error. At least one route must be specified'); | ||
} | ||
if ( !this.options.routes ) { | ||
throw new Error( 'Config error. At least one route must be specified' ); | ||
} | ||
this.options.routes.forEach((routeSpec) => { | ||
if (routeSpec.rule && /^\/.+\/$/.test(routeSpec.rule)) { | ||
const regExp = new RegExp(routeSpec.rule.substring(1, routeSpec.rule.length - 1)); | ||
routeSpec.rule = url => regExp.test(url); | ||
} else { | ||
routeSpec.rule = () => true; | ||
} | ||
}); | ||
this.options.routes.forEach( ( routeSpec ) => { | ||
if ( routeSpec.rule && /^\/.+\/$/.test( routeSpec.rule ) ) { | ||
const regExp = new RegExp( routeSpec.rule | ||
.substring( 1, routeSpec.rule.length - 1 ) ); | ||
routeSpec.rule = ( url ) => regExp.test( url ); | ||
} else { | ||
routeSpec.rule = () => true; | ||
} | ||
} ); | ||
this.options.multicast_ttl = this.options.multicast_ttl || 8; | ||
this.seqReqId = 1; | ||
this.socket = dgram.createSocket('udp4'); | ||
} | ||
this.options.multicast_ttl = this.options.multicast_ttl || 8; | ||
this.seqReqId = 1; | ||
this.socket = dgram.createSocket( 'udp4' ); | ||
} | ||
bind() { | ||
return this.socket.bindAsync({ exclusive: true }) | ||
.then(() => { | ||
this.socket.setMulticastLoopback(false); | ||
this.socket.setMulticastTTL(this.options.multicast_ttl); | ||
}); | ||
} | ||
bind() { | ||
return this.socket.bindAsync( { exclusive: true } ) | ||
.then( () => { | ||
this.socket.setMulticastLoopback( false ); | ||
this.socket.setMulticastTTL( this.options.multicast_ttl ); | ||
} ); | ||
} | ||
/** | ||
/** | ||
* Purge a list of resources cahced under provided URLs | ||
@@ -57,22 +57,23 @@ * @param {Array} urls array of urls to purge | ||
*/ | ||
purge(urls) { | ||
return P.all(urls.map((url) => { | ||
const datagram = this._constructHTCPRequest(url); | ||
const route = this._lookupRoute(url); | ||
if (route) { | ||
return this.socket.sendAsync(datagram, 0, datagram.length, route.port, route.host); | ||
} else { | ||
return P.resolve(); | ||
} | ||
})); | ||
} | ||
purge( urls ) { | ||
return P.all( urls.map( ( url ) => { | ||
const datagram = this._constructHTCPRequest( url ); | ||
const route = this._lookupRoute( url ); | ||
if ( route ) { | ||
return this.socket | ||
.sendAsync( datagram, 0, datagram.length, route.port, route.host ); | ||
} else { | ||
return P.resolve(); | ||
} | ||
} ) ); | ||
} | ||
close() { | ||
try { | ||
return this.socket.close(); | ||
} catch (e) { | ||
// We've tried, but seems like socket is already closed, so swallow the error. | ||
} | ||
} | ||
/** | ||
close() { | ||
try { | ||
return this.socket.close(); | ||
} catch ( e ) { | ||
// We've tried, but seems like socket is already closed, so swallow the error. | ||
} | ||
} | ||
/** | ||
* Construct a UDP datagram with HTCP packet for Varnish flush of the url | ||
@@ -83,42 +84,42 @@ * @param {string} url a url of the resource that should be flushed | ||
*/ | ||
_constructHTCPRequest(url) { | ||
const urlByteLen = Buffer.byteLength(url); | ||
const htcpSpecifierLen = 2 + 4 + 2 + urlByteLen + 2 + 8 + 2; | ||
const htcpDataLen = 8 + 2 + htcpSpecifierLen; | ||
const htcpLen = 4 + htcpDataLen + 2; | ||
_constructHTCPRequest( url ) { | ||
const urlByteLen = Buffer.byteLength( url ); | ||
const htcpSpecifierLen = 2 + 4 + 2 + urlByteLen + 2 + 8 + 2; | ||
const htcpDataLen = 8 + 2 + htcpSpecifierLen; | ||
const htcpLen = 4 + htcpDataLen + 2; | ||
const result = new Buffer(htcpLen); | ||
// Length | ||
result.writeInt16BE(htcpLen, 0); | ||
// Major-minor version | ||
result.writeInt16BE(0, 2); | ||
// Data length | ||
result.writeInt16BE(htcpDataLen, 4); | ||
// Op code & response | ||
result.writeInt8(4, 6); | ||
// Reserved & flags | ||
result.writeInt8(0, 7); | ||
// Transaction Id - seq number of a a request | ||
result.writeInt32BE(this.seqReqId++, 8); | ||
const result = Buffer.alloc( htcpLen ); | ||
// Length | ||
result.writeUInt16BE( htcpLen, 0 ); | ||
// Major-minor version | ||
result.writeUInt16BE( 0, 2 ); | ||
// Data length | ||
result.writeUInt16BE( htcpDataLen, 4 ); | ||
// Op code & response | ||
result.writeUInt8( 4, 6 ); | ||
// Reserved & flags | ||
result.writeUInt8( 0, 7 ); | ||
// Transaction Id - seq number of a request | ||
this.seqReqId &= 0xFFFFFFFF; | ||
result.writeUInt32BE( this.seqReqId++, 8 ); | ||
// HTCP packet contents - CLR specifier | ||
// Reserved & reason | ||
result.writeUInt16BE( 0, 12 ); | ||
// COUNTSTR method: length + method (HEAD & GET are equivalent) | ||
result.writeUInt16BE( 4, 14 ); | ||
result.write( 'HEAD', 16, 4 ); | ||
// COUNTSTR uri: length + URI | ||
result.writeUInt16BE( urlByteLen, 20 ); | ||
result.write( url, 22, urlByteLen ); | ||
// COUNTSTR version: length + http version | ||
result.writeUInt16BE( 8, 22 + urlByteLen ); | ||
result.write( 'HTTP/1.0', 24 + urlByteLen, 8 ); | ||
// COUNTSTR headers: empty, use just as padding | ||
result.writeUInt16BE( 0, 32 + urlByteLen ); | ||
result.writeUInt16BE( 2, 14 + htcpSpecifierLen ); | ||
// HTCP packet contents - CLR specifier | ||
// Reserved & reason | ||
result.writeInt16BE(0, 12); | ||
// COUNTSTR method: length + method (HEAD & GET are equivalent) | ||
result.writeInt16BE(4, 14); | ||
result.write('HEAD', 16, 4); | ||
// COUNTSTR uri: length + URI | ||
result.writeInt16BE(urlByteLen, 20); | ||
result.write(url, 22, urlByteLen); | ||
// COUNTSTR version: length + http version | ||
result.writeInt16BE(8, 22 + urlByteLen); | ||
result.write('HTTP/1.0', 24 + urlByteLen, 8); | ||
// COUNTSTR headers: empty, use just as padding | ||
result.writeInt16BE(0, 32 + urlByteLen); | ||
result.writeInt16BE(2, 14 + htcpSpecifierLen); | ||
return result; | ||
} | ||
return result; | ||
} | ||
/** | ||
/** | ||
* Lookup a cache endpoint for a concrete URL, based on options | ||
@@ -130,17 +131,17 @@ * supplied in constructor | ||
*/ | ||
_lookupRoute(url) { | ||
const route = this.options.routes.find(route => route.rule(url)); | ||
if (!route) { | ||
this.log('error/htcp-purge', { | ||
msg: `Could not find route for ${url}` | ||
}); | ||
return undefined; | ||
} | ||
return { | ||
host: route.host, | ||
port: route.port | ||
}; | ||
} | ||
_lookupRoute( url ) { | ||
const route = this.options.routes.find( ( route ) => route.rule( url ) ); | ||
if ( !route ) { | ||
this.log( 'error/htcp-purge', { | ||
msg: `Could not find route for ${url}` | ||
} ); | ||
return undefined; | ||
} | ||
return { | ||
host: route.host, | ||
port: route.port | ||
}; | ||
} | ||
} | ||
module.exports = HTCPPurger; |
@@ -1,34 +0,34 @@ | ||
"use strict"; | ||
'use strict'; | ||
/* eslint no-console: ["error", { allow: ["log"] }] */ | ||
const HTCPPurger = require('../index.js'); | ||
const HTCPPurger = require( '../index.js' ); | ||
function validateArgs() { | ||
if (process.argv.length !== 4) { | ||
return false; | ||
} | ||
const varnishIP = process.argv[2]; | ||
return /(?:\d{1,3}\.){3}\d{1,3}:\d{4}/.test(varnishIP); | ||
if ( process.argv.length !== 4 ) { | ||
return false; | ||
} | ||
const varnishIP = process.argv[ 2 ]; | ||
return /(?:\d{1,3}\.){3}\d{1,3}:\d{4}/.test( varnishIP ); | ||
} | ||
if (!validateArgs()) { | ||
console.log('Usage: node purge.js <varnish ip:port> <resource uri>'); | ||
process.exit(1); | ||
if ( !validateArgs() ) { | ||
console.log( 'Usage: node purge.js <varnish ip:port> <resource uri>' ); | ||
process.exit( 1 ); | ||
} | ||
const varnishIP = process.argv[2]; | ||
const purgeURL = process.argv[3]; | ||
const hostPortMatch = varnishIP.match(/((?:\d{1,3}\.){3}\d{1,3}):(\d{4})/); | ||
const varnishHostIp = hostPortMatch[1]; | ||
const varnishPort = parseInt(hostPortMatch[2]); | ||
const varnishIP = process.argv[ 2 ]; | ||
const purgeURL = process.argv[ 3 ]; | ||
const hostPortMatch = varnishIP.match( /((?:\d{1,3}\.){3}\d{1,3}):(\d{4})/ ); | ||
const varnishHostIp = hostPortMatch[ 1 ]; | ||
const varnishPort = parseInt( hostPortMatch[ 2 ] ); | ||
const purger = new HTCPPurger({ | ||
log: console.log.bind(console), | ||
routes: [{ | ||
host: varnishHostIp, | ||
port: varnishPort | ||
}] | ||
}); | ||
const purger = new HTCPPurger( { | ||
log: console.log.bind( console ), | ||
routes: [ { | ||
host: varnishHostIp, | ||
port: varnishPort | ||
} ] | ||
} ); | ||
console.log(`Sending a datagram to ${varnishHostIp}:${varnishPort} for uri ${purgeURL}`); | ||
purger.bind().then(() => purger.purge([purgeURL])).delay(100).then(() => purger.close()); | ||
console.log( `Sending a datagram to ${varnishHostIp}:${varnishPort} for uri ${purgeURL}` ); | ||
purger.bind().then( () => purger.purge( [ purgeURL ] ) ).delay( 100 ).then( () => purger.close() ); |
{ | ||
"name": "htcp-purge", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Varnish caches purging method", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha", | ||
"coverage": "istanbul cover _mocha -- -R spec", | ||
"test": "npm run lint && mocha", | ||
"lint": "eslint --max-warnings 0 .", | ||
"coverage": "nyc --reporter=lcov npm test", | ||
"coveralls": "cat ./coverage/lcov.info | coveralls" | ||
@@ -29,12 +30,10 @@ }, | ||
"devDependencies": { | ||
"istanbul": "^0.4.5", | ||
"mocha": "^5.1.0", | ||
"mocha-jshint": "^2.3.1", | ||
"eslint": "^5.16.0", | ||
"eslint-config-wikimedia": "^0.13.1", | ||
"eslint-plugin-jsdoc": "^15.8.0", | ||
"eslint-plugin-json": "^2.0.1", | ||
"mocha": "^6.2.0", | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"mocha-eslint": "^4.1.0", | ||
"eslint-config-node-services": "^2.2.5", | ||
"eslint-config-wikimedia": "^0.5.0", | ||
"eslint-plugin-jsdoc": "^3.6.3", | ||
"eslint-plugin-json": "^1.2.0" | ||
"nyc": "^15.0.0" | ||
} | ||
} |
@@ -1,113 +0,126 @@ | ||
"use strict"; | ||
'use strict'; | ||
require('mocha-jshint')(); | ||
require('mocha-eslint')([ 'index.js' ]); | ||
const HTCPPurger = require( '../index' ); | ||
const assert = require( 'assert' ); | ||
const dgram = require( 'dgram' ); | ||
const HTCPPurger = require('../index'); | ||
const assert = require('assert'); | ||
const dgram = require('dgram'); | ||
describe( 'Protocol tests', () => { | ||
const referenceBuffer = Buffer.from( [ 0, 44, 0, 0, 0, 38, 4, 0, | ||
0, 0, 0, 1, 0, 0, 0, 4, 72, 69, 65, 68, 0, 8, | ||
116, 101, 115, 116, 46, 99, 111, 109, 0, 8, 72, | ||
84, 84, 80, 47, 49, 46, 48, 0, 0, 0, 2 ] ); | ||
const referenceBuffer2 = Buffer.from( [ 0, 44, 0, 0, 0, 38, 4, 0, | ||
0, 0, 0, 2, 0, 0, 0, 4, 72, 69, 65, 68, 0, 8, | ||
116, 101, 115, 116, 46, 99, 111, 109, 0, 8, 72, | ||
84, 84, 80, 47, 49, 46, 48, 0, 0, 0, 2 ] ); | ||
describe('Protocol tests', () => { | ||
const referenceBuffer = new Buffer([0, 44, 0, 0, 0, 38, 4, 0, | ||
0, 0, 0, 1, 0, 0, 0, 4, 72, 69, 65, 68, 0, 8, | ||
116, 101, 115, 116, 46, 99, 111, 109, 0, 8, 72, | ||
84, 84, 80, 47, 49, 46, 48, 0, 0, 0, 2]); | ||
const referenceBuffer2 = new Buffer([0, 44, 0, 0, 0, 38, 4, 0, | ||
0, 0, 0, 2, 0, 0, 0, 4, 72, 69, 65, 68, 0, 8, | ||
116, 101, 115, 116, 46, 99, 111, 109, 0, 8, 72, | ||
84, 84, 80, 47, 49, 46, 48, 0, 0, 0, 2]); | ||
it( 'should construct correct datagram', () => { | ||
const purger = new HTCPPurger( { | ||
routes: [ | ||
{ | ||
host: 'default', | ||
port: 4827 | ||
} | ||
] | ||
} ); | ||
const resultDatagram = purger._constructHTCPRequest( 'test.com' ); | ||
assert.deepEqual( referenceBuffer, resultDatagram ); | ||
} ); | ||
it('should construct correct datagram', () => { | ||
const purger = new HTCPPurger({ | ||
routes: [ | ||
{ | ||
host: 'default', | ||
port: 4827 | ||
} | ||
] | ||
}); | ||
const resultDatagram = purger._constructHTCPRequest('test.com'); | ||
assert.deepEqual(referenceBuffer, resultDatagram); | ||
}); | ||
it( 'should lookup route by regex', () => { | ||
const purger = new HTCPPurger( { | ||
routes: [ | ||
{ | ||
rule: '/https?:\\/\\/test\\.com/', | ||
host: '123.123.123.123', | ||
port: 1234 | ||
}, | ||
{ | ||
host: 'default', | ||
port: 1234 | ||
} | ||
] | ||
} ); | ||
const route = purger._lookupRoute( 'http://test.com' ); | ||
assert.deepEqual( '123.123.123.123', route.host ); | ||
assert.deepEqual( 1234, route.port ); | ||
const route2 = purger._lookupRoute( 'http://test2.com' ); | ||
assert.deepEqual( 'default', route2.host ); | ||
assert.deepEqual( 1234, route2.port ); | ||
} ); | ||
it ('should lookup route by regex', () => { | ||
const purger = new HTCPPurger({ | ||
routes: [ | ||
{ | ||
rule: '/https?:\\/\\/test\\.com/', | ||
host: '123.123.123.123', | ||
port: 1234 | ||
}, | ||
{ | ||
host: 'default', | ||
port: 1234 | ||
} | ||
] | ||
}); | ||
const route = purger._lookupRoute('http://test.com'); | ||
assert.deepEqual('123.123.123.123', route.host); | ||
assert.deepEqual(1234, route.port); | ||
const route2 = purger._lookupRoute('http://test2.com'); | ||
assert.deepEqual('default', route2.host); | ||
assert.deepEqual(1234, route2.port); | ||
}); | ||
it( 'should send datagrams', function ( done ) { | ||
this.timeout( 5000 ); | ||
const purger = new HTCPPurger( { | ||
routes: [ | ||
{ | ||
host: 'localhost', | ||
port: 12345 | ||
} | ||
] | ||
} ); | ||
const server = dgram.createSocket( 'udp4' ); | ||
server.on( 'message', ( msg ) => { | ||
assert.deepEqual( referenceBuffer, msg ); | ||
done(); | ||
} ); | ||
server.bind( 12345 ); | ||
purger | ||
.bind() | ||
.then( () => purger.purge( [ 'test.com' ] ) ) | ||
.delay( 100 ) | ||
.finally( () => { | ||
purger.close(); | ||
server.close(); | ||
} ); | ||
} ); | ||
it ('should send datagrams' ,function(done) { | ||
this.timeout(5000); | ||
const purger = new HTCPPurger({ | ||
routes: [ | ||
{ | ||
host: 'localhost', | ||
port: 12345 | ||
} | ||
] | ||
}); | ||
const server = dgram.createSocket('udp4'); | ||
server.on("message", msg => { | ||
assert.deepEqual(referenceBuffer, msg); | ||
done(); | ||
}); | ||
server.bind(12345); | ||
purger | ||
.bind() | ||
.then(() => purger.purge(['test.com'])) | ||
.delay(100) | ||
.finally(() => { | ||
purger.close(); | ||
server.close(); | ||
}); | ||
}); | ||
it( 'should increase seq num of datagrams', ( done ) => { | ||
const purger = new HTCPPurger( { | ||
routes: [ | ||
{ | ||
host: 'localhost', | ||
port: 12346 | ||
} | ||
] | ||
} ); | ||
const server = dgram.createSocket( 'udp4' ); | ||
let msgIdx = 1; | ||
server.on( 'message', ( msg ) => { | ||
if ( msgIdx === 1 ) { | ||
assert.deepEqual( referenceBuffer, msg ); | ||
msgIdx++; | ||
} else { | ||
assert.deepEqual( referenceBuffer2, msg ); | ||
done(); | ||
} | ||
} ); | ||
server.bind( 12346 ); | ||
it ('should increase seq num of datagrams' ,done => { | ||
const purger = new HTCPPurger({ | ||
routes: [ | ||
{ | ||
host: 'localhost', | ||
port: 12346 | ||
} | ||
] | ||
}); | ||
const server = dgram.createSocket('udp4'); | ||
let msgIdx = 1; | ||
server.on("message", msg => { | ||
if (msgIdx === 1) { | ||
assert.deepEqual(referenceBuffer, msg); | ||
msgIdx++; | ||
} else { | ||
assert.deepEqual(referenceBuffer2, msg); | ||
done(); | ||
} | ||
}); | ||
server.bind(12346); | ||
purger.bind() | ||
.then( () => purger.purge( [ 'test.com' ] ) ) | ||
.delay( 100 ) | ||
.then( () => purger.purge( [ 'test.com' ] ) ) | ||
.delay( 100 ) | ||
.then( () => { | ||
purger.close(); | ||
server.close(); | ||
} ); | ||
} ); | ||
purger.bind() | ||
.then(() => purger.purge(['test.com'])) | ||
.delay(100) | ||
.then(() => purger.purge(['test.com'])) | ||
.delay(100) | ||
.then(() => { | ||
purger.close(); | ||
server.close(); | ||
}) | ||
}); | ||
}); | ||
it( 'should reset seqReqId after max 32-int is reached', () => { | ||
const purger = new HTCPPurger( { | ||
routes: [ | ||
{ | ||
host: 'localhost', | ||
port: 999 | ||
} | ||
] | ||
} ); | ||
purger.seqReqId = 0xFFFFFFFF + 1; | ||
purger._constructHTCPRequest( 'test.com' ); | ||
assert.equal( purger.seqReqId, 1 ); | ||
} ); | ||
} ); |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
7
274
10188
8
3