Skip to content

Commit

Permalink
Add checking for proper header characters.
Browse files Browse the repository at this point in the history
Add module for doing things with headers.
Fix up tests that catch errors.
  • Loading branch information
yakovkhalinsky committed Jan 2, 2016
1 parent 3f031ed commit e27d468
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 61 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### v0.9.6 (December 18, 2015) - The information release

Features
- Add support for adding X-Bz-Info-* headers when uploading a file [link](https://github.com/yakovkhalinsky/backblaze-b2/pull/6)


### v0.9.5 (December 18, 2015) - The string encoding release

Features
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ Always run `npm test` before you commit.
uploadAuthToken: 'uploadAuthToken',
filename: 'filename',
data: 'data' // this is expecting a Buffer not an encoded string,
info: { // optional info headers, prepended with X-Bz-Info- when sent, throws error if more than 10 keys set
info: {
// optional info headers, prepended with X-Bz-Info- when sent, throws error if more than 10 keys set
// valid characters should be a-z, A-Z and '-', all other characters will cause an error to be thrown
key1: value
key2: value
}
Expand Down
19 changes: 2 additions & 17 deletions lib/actions/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var sha1 = require('node-sha1');
var q = require('q');

var utils = require('./../utils');
var headers = require('../headers');
var request = require('../request');
var conf = require('../../conf');

Expand All @@ -23,7 +24,7 @@ exports.uploadFile = function(b2, args) {
},
body: data
};
addInfoHeaders(options, info);
headers.addInfoHeaders(options, info);
return request.sendRequest(options);
};

Expand Down Expand Up @@ -162,22 +163,6 @@ function processDownloadResponse(response, body) {
return obj;
}

function addInfoHeaders(options, info) {
var MAX_INFO_HEADERS = conf.MAX_INFO_HEADERS;
if (info) {
var keys = Object.keys(info);
if (keys.length > MAX_INFO_HEADERS) {
throw new Error('Too many info headers: maximum of ' + MAX_INFO_HEADERS + ' allowed');
}
keys.forEach(addInfoHeader);
}

function addInfoHeader(infoKey) {
var key = 'X-Bz-Info-' + encodeURIComponent(infoKey);
options.headers[key] = info[infoKey];
}
}

function getListFilesUrl(b2) {
return getApiUrl(b2) + '/b2_list_file_names';
}
Expand Down
32 changes: 32 additions & 0 deletions lib/headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var conf = require('../conf');

exports.addInfoHeaders = function(options, info) {
var MAX_INFO_HEADERS = conf.MAX_INFO_HEADERS;
var invalidKeys = [];
if (info) {
var keys = Object.keys(info);

if (keys.length > MAX_INFO_HEADERS) {
throw new Error('Too many info headers: maximum of ' + MAX_INFO_HEADERS + ' allowed');
}

keys.forEach(addInfoHeader);

if (invalidKeys.length) {
throw new Error('Info header keys contain invalid characters: ' + invalidKeys.join(' '));
}
}

function isValidHeader(header) {
return /^[a-z0-9\-]+$/i.test(header);
}

function addInfoHeader(infoKey) {
if (isValidHeader(infoKey)) {
var key = 'X-Bz-Info-' + infoKey;
options.headers[key] = encodeURIComponent(info[infoKey]);
} else {
return invalidKeys.push(infoKey);
}
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backblaze-b2",
"version": "0.9.5",
"version": "0.9.6",
"description": "Node.js Library for the Backblaze B2 Storage Service",
"main": "index.js",
"scripts": {
Expand Down
40 changes: 2 additions & 38 deletions test/unit/lib/actions/fileTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ describe('actions/file', function() {
data: 'some text file content',
info: {
foo: 'bar',
unicorns: 'rainbows',
'unicorns-and_rainbows!@#$%^&': 'header key with funky characters'
unicorns: 'rainbows'
}
};

Expand All @@ -115,49 +114,14 @@ describe('actions/file', function() {
'X-Bz-File-Name': 'foo.txt',
'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22',
'X-Bz-Info-foo': 'bar',
'X-Bz-Info-unicorns': 'rainbows',
'X-Bz-Info-unicorns-and_rainbows!%40%23%24%25%5E%26': 'header key with funky characters'
'X-Bz-Info-unicorns': 'rainbows'
},
body: 'some text file content'
});
});

});

describe('with more than maximum number of allowed info headers', function() {

beforeEach(function() {
options = {
uploadUrl: 'https://uploadUrl',
uploadAuthToken: 'uploadauthtoken',
filename: 'foo.txt',
data: 'some text file content',
info: {
a: 'foo',
b: 'foo',
c: 'foo',
d: 'foo',
e: 'foo',
f: 'foo',
g: 'foo',
h: 'foo',
i: 'foo',
j: 'foo',
k: 'foo'
}
};
});

it('should throw an error', function() {
try {
file.uploadFile(b2, options);
} catch (e) {
expect(e.message).to.be('Too many info headers: maximum of 10 allowed');
}
});

});

});


Expand Down
78 changes: 78 additions & 0 deletions test/unit/lib/headersTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
var expect = require('expect.js');

var headers = require('../../../lib/headers');

describe('headers', function() {

var options;
var info;
var err;

beforeEach(function() {
options = {
headers: {}
};

info = null;

err = {
message: ''
};
});

describe('addInfoHeaders', function() {

it('should add the provided key/values to the headers properties of the options argument', function() {
info = {
foo: 'bar',
unicorns: 'rainbows'
};

headers.addInfoHeaders(options, info);

expect(options.headers).to.eql({
'X-Bz-Info-foo': 'bar',
'X-Bz-Info-unicorns': 'rainbows'
});
});

it('should throw an error when too many info headers are added', function() {
info = {
a: 'foo',
b: 'foo',
c: 'foo',
d: 'foo',
e: 'foo',
f: 'foo',
g: 'foo',
h: 'foo',
i: 'foo',
j: 'foo',
k: 'foo'
};

try {
headers.addInfoHeaders(options, info);
} catch (e) {
err = e;
}
expect(err.message).to.be('Too many info headers: maximum of 10 allowed');
});

it('should throw an error with a list of keys that have invalid characters', function() {
info = {
'abcsABC-!@#$_': 'woh bad key',
'defgDEF-)(*&^:"': 'very bad key'
};

try {
headers.addInfoHeaders(options, info);
} catch (e) {
err = e;
}
expect(err.message).to.be('Info header keys contain invalid characters: abcsABC-!@#$_ defgDEF-)(*&^:"');
});

});

});
20 changes: 16 additions & 4 deletions test/unit/lib/utilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ var utils = require('../../../lib/utils');

describe('utils', function() {

var err;

beforeEach(function() {
err = {
message: ''
};
});

describe('getAuthHeaderObject', function() {
it('Should get header object with Base64 encoded Authorization header', function() {
var accountId = 'unicorns';
Expand All @@ -18,8 +26,9 @@ describe('utils', function() {
try {
utils.getAuthHeaderObject(accountId, applicationKey);
} catch (e) {
expect(e.message).to.be('Invalid accountId');
err = e;
}
expect(err.message).to.be('Invalid accountId');
});

it('Should throw error given invalid applicationKey', function() {
Expand All @@ -28,8 +37,9 @@ describe('utils', function() {
try {
utils.getAuthHeaderObject(accountId, applicationKey);
} catch (e) {
expect(e.message).to.be('Invalid applicationKey');
err = e;
}
expect(err.message).to.be('Invalid applicationKey');
});
});

Expand All @@ -47,17 +57,19 @@ describe('utils', function() {
try {
utils.getAuthHeaderObjectWithToken(b2);
} catch (e) {
expect(e.message).to.be('Invalid B2 instance');
err = e;
}
expect(err.message).to.be('Invalid B2 instance');
});

it('Should throw error given b2 object has no valid authorizationToken', function() {
var b2 = {};
try {
utils.getAuthHeaderObjectWithToken(b2);
} catch (e) {
expect(e.message).to.be('Invalid authorizationToken');
err = e;
}
expect(err.message).to.be('Invalid authorizationToken');
});
});

Expand Down

0 comments on commit e27d468

Please sign in to comment.