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

Added error messages to abortWebSocketUpgrade() #1741

Merged
merged 10 commits into from
Feb 25, 2020
16 changes: 10 additions & 6 deletions libraries/botbuilder/src/botFrameworkAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1216,10 +1216,14 @@ export class BotFrameworkAdapter extends BotAdapter implements IUserTokenProvide
} catch (err) {
// If the authenticateConnection call fails, send back the correct error code and close
// the connection.
if (typeof(err.message) === 'string' && err.message.toLowerCase().startsWith('unauthorized')) {
abortWebSocketUpgrade(socket, 401);
} else if (typeof(err.message) === 'string' && err.message.toLowerCase().startsWith(`'authheader'`)) {
abortWebSocketUpgrade(socket, 400);
if (typeof(err.message) === 'string') {
if (err.message.toLowerCase().startsWith('unauthorized')) {
abortWebSocketUpgrade(socket, 401, err.message);
} else if (err.message.toLowerCase().startsWith(`'authheader'`)) {
abortWebSocketUpgrade(socket, 400, err.message);
} else {
abortWebSocketUpgrade(socket, 500, err.message);
}
} else {
abortWebSocketUpgrade(socket, 500);
}
Expand Down Expand Up @@ -1355,10 +1359,10 @@ function delay(timeout: number): Promise<void> {
});
}

function abortWebSocketUpgrade(socket: INodeSocket, code: number) {
function abortWebSocketUpgrade(socket: INodeSocket, code: number, message?: string) {
if (socket.writable) {
const connectionHeader = `Connection: 'close'\r\n`;
socket.write(`HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n${connectionHeader}\r\n`);
socket.write(`HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n${message}\r\n${connectionHeader}\r\n`);
}

socket.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,50 @@ describe('BotFrameworkAdapter Streaming tests', () => {
await bot.run(context);
throw new Error('useWebSocket should have thrown an error');
}).catch(err => {
expect(err.message).to.equal('Unauthorized. No valid identity.');
const socketResponse = MockNetSocket.createNonSuccessResponse(401);
expect(err.message).to.equal('Unauthorized. Is not authenticated');
const socketResponse = MockNetSocket.createNonSuccessResponse(401, err.message);
expect(writeSpy.called).to.be.true;
expect(writeSpy.calledWithExactly(socketResponse)).to.be.true;
expect(destroySpy.calledOnceWithExactly()).to.be.true;
});
});

it('returns status code 400 when request is missing Authorization header', async () => {
const bot = new ActivityHandler();
settings = new TestAdapterSettings('appId', 'password');
const adapter = new BotFrameworkAdapter(settings);
const requestWithoutAuthHeader = new MockHttpRequest();

const socket = new MockNetSocket();
const writeSpy = spy(socket, 'write');
const destroySpy = spy(socket, 'destroy');

await adapter.useWebSocket(requestWithoutAuthHeader, socket, Buffer.from([]), async (context) => {
await bot.run(context);

throw new Error('useWebSocket should have thrown an error');
}).catch(err => {
Zerryth marked this conversation as resolved.
Show resolved Hide resolved
expect(err.message).to.equal("'authHeader' required.");
const socketResponse = MockNetSocket.createNonSuccessResponse(400, err.message);
expect(writeSpy.called).to.be.true;
expect(writeSpy.calledWithExactly(socketResponse)).to.be.true;
expect(destroySpy.calledOnceWithExactly()).to.be.true;
});
});

it('returns status code 500 when request logic is not callable', async () => {
const adapter = new BotFrameworkAdapter(new TestAdapterSettings());
const request = new MockHttpRequest();
const socket = new MockNetSocket();

const useWebSocketSpy = spy(adapter, 'useWebSocket');
const uncallableLogic = null;

await adapter.useWebSocket(request, socket, Buffer.from([]), uncallableLogic).catch(err => {
expect(err.message).to.equal('Streaming logic needs to be provided to `useWebSocket`');
expect(useWebSocketSpy.called).to.be.true;
});
});
});

describe('processRequest()', () => {
Expand Down
4 changes: 2 additions & 2 deletions libraries/botbuilder/tests/streaming/mockNetSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class MockNetSocket {
destroy(err) { }
}

MockNetSocket.createNonSuccessResponse = (code) => {
return `HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\nConnection: 'close'\r\n\r\n`;
MockNetSocket.createNonSuccessResponse = (code, message) => {
return `HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n${message}\r\nConnection: 'close'\r\n\r\n`;
};

module.exports.MockNetSocket = MockNetSocket;