Properly handling POST bodies in hyco-https plus test coverage (#22)
* handling body content on control channel * unit tests and appveyor setup
This commit is contained in:
Родитель
5f71bb26c9
Коммит
c37436a51f
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "simple",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
|
@ -117,7 +117,7 @@ util.inherits(ServerResponse, OutgoingMessage);
|
|||
|
||||
ServerResponse.prototype._finish = function _finish() {
|
||||
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
|
||||
COUNTER_HTTP_SERVER_RESPONSE();
|
||||
//COUNTER_HTTP_SERVER_RESPONSE();
|
||||
OutgoingMessage.prototype._finish.call(this);
|
||||
};
|
||||
|
||||
|
@ -286,6 +286,7 @@ function Server(options, requestListener) {
|
|||
this.listenUri = listenUri + '&id=' + options.id;
|
||||
}
|
||||
|
||||
this.pendingRequest = null;
|
||||
this.closeRequested = false;
|
||||
this.options = options;
|
||||
this.path = options.path;
|
||||
|
@ -399,6 +400,13 @@ function connectControlChannel(server) {
|
|||
}
|
||||
|
||||
server.controlChannel.onmessage = function(event) {
|
||||
|
||||
if ( server.pendingRequest != null ) {
|
||||
server.pendingRequest.handleBody(event.data);
|
||||
server.pendingRequest = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var message = JSON.parse(event.data);
|
||||
|
||||
if (isDefinedAndNonNull(message, 'accept')) {
|
||||
|
@ -565,6 +573,13 @@ function controlChannelRequest(server, message) {
|
|||
// handler to call when the connection sequence completes
|
||||
var self = server;
|
||||
|
||||
if ( message.request.method) {
|
||||
req = new IncomingMessage(message, server.controlChannel);
|
||||
if ( message.request.body == true) {
|
||||
self.pendingRequest = req;
|
||||
}
|
||||
}
|
||||
|
||||
// execute the web socket rendezvous with the server
|
||||
try {
|
||||
var client = new WebSocket(address, {
|
||||
|
@ -586,14 +601,11 @@ function controlChannelRequest(server, message) {
|
|||
|
||||
// do we have a request or is this just rendezvous?
|
||||
if ( message.request.method) {
|
||||
var req = new IncomingMessage(message, server.controlChannel);
|
||||
var res = new ServerResponse(req);
|
||||
res.requestId = message.request.id;
|
||||
res.assignSocket(client);
|
||||
server.emit('request', req, res);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
client.on('error', function(event) {
|
||||
|
@ -616,6 +628,9 @@ function requestChannelRequest(channel, message) {
|
|||
var res = null;
|
||||
// do we have a request or is this just rendezvous?
|
||||
var req = new IncomingMessage(message, channel);
|
||||
if ( message.request.body == true) {
|
||||
channel.pendingRequest = req;
|
||||
}
|
||||
res = new ServerResponse(req);
|
||||
res.requestId = message.request.id;
|
||||
res.assignSocket(channel);
|
||||
|
@ -626,7 +641,15 @@ function requestChannelRequest(channel, message) {
|
|||
}
|
||||
|
||||
function requestChannelListener(server, requestChannel) {
|
||||
|
||||
requestChannel.onmessage = function(event) {
|
||||
|
||||
if ( requestChannel.pendingRequest != null )
|
||||
{
|
||||
requestChannel.pendingRequest.handleBody(event.data);
|
||||
requestChannel.pendingRequest = null;
|
||||
return;
|
||||
}
|
||||
var message = JSON.parse(event.data);
|
||||
|
||||
if (isDefinedAndNonNull(message, 'request')) {
|
||||
|
|
|
@ -26,7 +26,7 @@ const Stream = require('stream');
|
|||
|
||||
/* Abstract base class for ServerRequest and ClientResponse. */
|
||||
function IncomingMessage(relayRequestMessage, relayWebSocket) {
|
||||
|
||||
Stream.Readable.call(this);
|
||||
this.socket = relayWebSocket;
|
||||
this.connection = relayWebSocket;
|
||||
|
||||
|
@ -56,7 +56,7 @@ function IncomingMessage(relayRequestMessage, relayWebSocket) {
|
|||
this._dumped = false;
|
||||
|
||||
for (var header in relayRequestMessage.request.requestHeaders) {
|
||||
this._addHeaderLine(header, relayRequestMessage.request.requestHeaders[header], this.headers);
|
||||
this._addHeaderLine(header, relayRequestMessage.request.requestHeaders[header], this.headers);
|
||||
}
|
||||
}
|
||||
util.inherits(IncomingMessage, Stream.Readable);
|
||||
|
@ -71,6 +71,12 @@ IncomingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
|
|||
|
||||
|
||||
IncomingMessage.prototype._read = function _read(n) {
|
||||
|
||||
};
|
||||
|
||||
IncomingMessage.prototype.handleBody = function handleBody(buf) {
|
||||
this.push(buf);
|
||||
this.push(null);
|
||||
};
|
||||
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,7 +2,7 @@
|
|||
"author": "Microsoft Corporation",
|
||||
"name": "hyco-https",
|
||||
"description": "HTTP with Azure Relay Hybrid Connections",
|
||||
"version": "1.0.7",
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"homepage": "https://docs.microsoft.com/en-us/azure/service-bus-relay/",
|
||||
|
@ -17,9 +17,15 @@
|
|||
"crypto": "latest",
|
||||
"events": "latest",
|
||||
"https": "latest",
|
||||
"moment": "^2.22.1",
|
||||
"moment": "^2.22.2",
|
||||
"util": "latest",
|
||||
"ws": "^5.1.1"
|
||||
"ws": "^6.0.0"
|
||||
},
|
||||
"gypfile": true
|
||||
"gypfile": true,
|
||||
"devDependencies": {
|
||||
"jest": "^23.4.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
var https = require('..')
|
||||
|
||||
jest.setTimeout(30000); // 30 seconds
|
||||
|
||||
test('HTTPS GET', (done) => {
|
||||
var ns = process.env.SB_HC_NAMESPACE ? process.env.SB_HC_NAMESPACE.replace(/^"(.*)"$/, '$1') : null;
|
||||
var path = "a1";
|
||||
var keyrule = process.env.SB_HC_KEYRULE ? process.env.SB_HC_KEYRULE.replace(/^"(.*)"$/, '$1') : null;
|
||||
var key = process.env.SB_HC_KEY ? process.env.SB_HC_KEY.replace(/^"(.*)"$/, '$1') : null;
|
||||
|
||||
expect(ns).toBeDefined();
|
||||
expect(path).toBeDefined();
|
||||
expect(keyrule).toBeDefined();
|
||||
expect(key).toBeDefined();
|
||||
|
||||
/* set up the listener */
|
||||
var uri = https.createRelayListenUri(ns, path);
|
||||
var server = https.createRelayedServer({
|
||||
server: uri,
|
||||
token: () => https.createRelayToken(uri, keyrule, key)
|
||||
},
|
||||
(req, res) => {
|
||||
expect(req.method).toBe('GET');
|
||||
expect(req.headers.custom).toBe('Hello');
|
||||
res.end('Hello');
|
||||
});
|
||||
|
||||
// fail we get an error
|
||||
server.listen((err) => {
|
||||
expect(err).toBeUndefined();
|
||||
});
|
||||
// fail if we get an error (we'll always get one if this triggers)
|
||||
server.on('error', (err) => {
|
||||
expect(err).toBeUndefined();
|
||||
});
|
||||
|
||||
// client is being run with 5 second delay to allow the server to settle
|
||||
setTimeout(() => {
|
||||
/* set up the client */
|
||||
var clientUri = https.createRelayHttpsUri(ns, path);
|
||||
var token = https.createRelayToken(clientUri, keyrule, key);
|
||||
https.get({
|
||||
hostname: ns,
|
||||
path: ((!path || path.length == 0 || path[0] !== '/') ? '/' : '') + path,
|
||||
port: 443,
|
||||
headers: {
|
||||
'ServiceBusAuthorization': token,
|
||||
'Custom': 'Hello'
|
||||
}
|
||||
}, (res) => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', (chunk) => {
|
||||
expect(chunk).toBe('Hello');
|
||||
});
|
||||
res.on('end', () => {
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
}).on('error', (e) => {
|
||||
expect(e).toBeUndefined();
|
||||
});
|
||||
}, 5000);
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
var https = require('..')
|
||||
|
||||
jest.setTimeout(30000); // 30 seconds
|
||||
|
||||
test('HTTP POST', (done) => {
|
||||
var ns = process.env.SB_HC_NAMESPACE?process.env.SB_HC_NAMESPACE.replace(/^"(.*)"$/, '$1'):null;
|
||||
var path = "a2";
|
||||
var keyrule = process.env.SB_HC_KEYRULE?process.env.SB_HC_KEYRULE.replace(/^"(.*)"$/, '$1'):null;
|
||||
var key = process.env.SB_HC_KEY?process.env.SB_HC_KEY.replace(/^"(.*)"$/, '$1'):null;
|
||||
|
||||
expect(ns).toBeDefined();
|
||||
expect(path).toBeDefined();
|
||||
expect(keyrule).toBeDefined();
|
||||
expect(key).toBeDefined();
|
||||
|
||||
/* set up the listener */
|
||||
var uri = https.createRelayListenUri(ns, path);
|
||||
var server = https.createRelayedServer({
|
||||
server: uri,
|
||||
token: () => https.createRelayToken(uri, keyrule, key)
|
||||
},
|
||||
(req, res) => {
|
||||
expect(req.method).toBe("POST");
|
||||
expect(req.headers.custom).toBe("Hello");
|
||||
req.setEncoding('utf-8');
|
||||
req.on('data', (chunk) => {
|
||||
expect(chunk).toBe('Hello');
|
||||
});
|
||||
req.on('end', () => {
|
||||
res.end('Hello');
|
||||
});
|
||||
});
|
||||
|
||||
// fail we get an error
|
||||
server.listen((err) => {
|
||||
expect(err).toBeUndefined();
|
||||
});
|
||||
// fail if we get an error (we'll always get one if this triggers)
|
||||
server.on('error', (err) => {
|
||||
expect(err).toBeUndefined();
|
||||
});
|
||||
|
||||
// client is being run with 5 second delay to allow the server to settle
|
||||
setTimeout(()=>{
|
||||
/* set up the client */
|
||||
var clientUri = https.createRelayHttpsUri(ns, path);
|
||||
var token = https.createRelayToken(clientUri, keyrule, key);
|
||||
|
||||
const postData = 'Hello';
|
||||
var req = https.request({
|
||||
hostname: ns,
|
||||
path: ((!path || path.length == 0 || path[0] !== '/') ? '/' : '') + path,
|
||||
port: 443,
|
||||
method : "POST",
|
||||
headers: {
|
||||
'ServiceBusAuthorization': token,
|
||||
'Custom' : 'Hello',
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': Buffer.byteLength(postData)
|
||||
}
|
||||
}, (res) => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', (chunk) => {
|
||||
expect(chunk).toBe('Hello');
|
||||
});
|
||||
res.on('end', () => {
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
}).on('error', (e) => {
|
||||
expect(e).toBeUndefined();
|
||||
});
|
||||
|
||||
req.write(postData);
|
||||
req.end();
|
||||
}, 5000);
|
||||
});
|
|
@ -0,0 +1,180 @@
|
|||
// For a detailed explanation regarding each configuration property, visit:
|
||||
// https://jestjs.io/docs/en/configuration.html
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after the first failure
|
||||
// bail: false,
|
||||
|
||||
// Respect "browser" field in package.json when resolving modules
|
||||
browser: false,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "C:\\Users\\clemensv\\AppData\\Local\\Temp\\jest",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
// clearMocks: false,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: null,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
// coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: null,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files usin a array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: null,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: null,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "always",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: null,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: null,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: null,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: null,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// The path to a module that runs some code to configure or set up the testing framework before each test
|
||||
// setupTestFrameworkScriptFile: null,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
// testMatch: [
|
||||
// "**/__tests__/**/*.js?(x)",
|
||||
// "**/?(*.)+(spec|test).js?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// The regexp pattern Jest uses to detect test files
|
||||
// testRegex: "",
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: null,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "about:blank",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: null,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: null,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
watchman: false,
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче