Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f84da5d
Fix typo in expires cookie validation error
theturtle32 May 19, 2025
6666483
Merge pull request #459 from theturtle32/codex/fix-typo-in-websocketr…
theturtle32 May 19, 2025
ef7d7c9
docs: clarify keepalive timeout behavior
theturtle32 May 19, 2025
2da1bd9
Merge pull request #460 from theturtle32/codex/expand-description-of-…
theturtle32 May 19, 2025
e77ccf5
Add tests for large WebSocket frame payloads
theturtle32 May 19, 2025
6a8783e
Merge pull request #461 from theturtle32/codex/add-tests-for-tobuffer…
theturtle32 May 19, 2025
fc92a7e
Phase 1: Complete test suite modernization with ES6 patterns
theturtle32 Jun 12, 2025
6e85cda
Update test/scripts/echo-server.js
theturtle32 Jun 12, 2025
10cf1c9
Address PR feedback: Complete arrow function conversions in test suite
theturtle32 Jun 12, 2025
e274ac7
Merge branch 'phase1-test-suite-modernization' of github.com:theturtl…
theturtle32 Jun 12, 2025
936847a
Complete remaining var → const/let conversions and fix ESLint issues
theturtle32 Jun 12, 2025
42db179
Adding .eslintrc.js
theturtle32 Jun 12, 2025
317f4e3
Complete migration from JSHint to ESLint with 2-space indentation
theturtle32 Jun 12, 2025
d826ff5
Complete Phase 1: Test Suite Modernization
theturtle32 Jun 12, 2025
3b7d6ef
Merge branch 'es6-refactor' into v2
theturtle32 Jun 12, 2025
afb79bd
Merge branch 'v2' into phase1-test-suite-modernization
theturtle32 Jun 12, 2025
cd1174c
Replace gulp with npm scripts for linting
theturtle32 Jun 12, 2025
e485355
Merge pull request #463 from theturtle32/phase1-test-suite-modernization
theturtle32 Jun 12, 2025
cc1ed44
Phase 2: Implement template literals, arrow functions, and destructuring
theturtle32 Jun 13, 2025
6c02de1
Fix GitHub Actions ESLint compatibility issue
theturtle32 Jun 13, 2025
15181d5
Fix code review issues: restore ES6+ modernizations
theturtle32 Jun 13, 2025
2096cac
Add Autobahn test results parser script
theturtle32 Jun 13, 2025
0ea2144
Add comprehensive Autobahn test runner
theturtle32 Jun 13, 2025
54ca978
Add Autobahn test script to package.json
theturtle32 Jun 13, 2025
68c277a
Only run on pull request
theturtle32 Jun 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ module.exports = {
extends: 'eslint:recommended',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
sourceType: 'script'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Changing sourceType from module to script might be a step back. Could you clarify the reasoning for this change?

},
rules: {
'indent': ['error', 2],
'linebreak-style': ['error', 'unix'],
'quotes': ['error', 'single'],
'semi': ['error', 'always'],
'no-unused-vars': ['error', { 'args': 'none' }],
'no-console': 'off'
'no-console': 'off',
'no-useless-escape': 'off',
'no-prototype-builtins': 'off',
'no-control-regex': 'off',
'no-empty': 'off',
'no-unsafe-finally': 'off'
Comment on lines +20 to +24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Disabling these ESLint rules might hide potential issues or reduce code clarity. Could you provide the rationale for disabling these rules?

Comment on lines +20 to +24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Disabling several ESLint rules might hide potential issues. Consider addressing the linting warnings by improving the code rather than disabling the rules globally. If specific lines legitimately require these patterns, consider using inline eslint-disable-next-line comments with explanations.

},
globals: {
'WebSocket': 'readonly'
'WebSocket': 'readonly',
'globalThis': 'readonly'
}
};
8 changes: 5 additions & 3 deletions .github/workflows/websocket-tests.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: websocket-tests
on: [push, pull_request]
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 10.x
node-version: 18.x

- uses: actions/checkout@v2

- run: npm install

- run: npm run lint

- run: npm run test

5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
## Build/Test Commands
- Run all tests: `npm test`
- Run single test: `npx tape test/unit/[filename].js`
- Lint codebase: `npm run gulp` or `npx gulp lint`
- Lint codebase: `npm run lint`
- Fix lint issues: `npm run lint:fix`
- Run autobahn tests: `cd test/autobahn && ./run-wstest.sh`

## Coding Style
- Use tabs for indentation
- Use 2 spaces for indentation
- Constants: ALL_CAPS with underscores
- Variables/Functions: camelCase
- Classes: PascalCase
Expand Down
56 changes: 28 additions & 28 deletions ES6_REFACTORING_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,29 @@ The ES6 refactoring is **partially complete**. The following core library files
### 1. **Unmodified Library Files** (1 file)
- `lib/version.js` - Already uses modern `module.exports`, no changes needed

### 2. **Test Suite Refactoring** (15 files)
**Priority: Medium** - Tests use old ES3/ES5 patterns

#### Unit Tests
- `test/unit/request.js` - Uses `var`, old-style functions
- `test/unit/dropBeforeAccept.js` - Needs var → const/let conversion
- `test/unit/regressions.js` - Old variable declarations
- `test/unit/w3cwebsocket.js` - var → const refactoring needed
- `test/unit/websocketFrame.js` - Old-style variable declarations

#### Test Infrastructure
- `test/shared/test-server.js` - Core test server utilities
- `test/shared/start-echo-server.js` - Echo server for tests

#### Test Scripts
- `test/scripts/memoryleak-server.js` - Memory leak testing
- `test/scripts/memoryleak-client.js` - Memory leak client
- `test/scripts/libwebsockets-test-server.js` - LibWebSockets compatibility
- `test/scripts/libwebsockets-test-client.js` - LibWebSockets client
- `test/scripts/fragmentation-test-client.js` - Fragmentation testing
- `test/scripts/fragmentation-test-server.js` - Fragmentation server
- `test/scripts/echo-server.js` - Basic echo server
- `test/scripts/autobahn-test-client.js` - Autobahn test suite client
### 2. **Test Suite Refactoring** ✅ **COMPLETED** (15 files)
**Status: Complete** - All test files modernized to ES6+ patterns

#### Unit Tests (5/5 Complete)
- `test/unit/request.js` - Modern const/let, arrow functions
- `test/unit/dropBeforeAccept.js` - Modern const/let, arrow functions
- `test/unit/regressions.js` - Modern const/let, arrow functions
- `test/unit/w3cwebsocket.js` - Modern const/let, arrow functions
- `test/unit/websocketFrame.js` - Modern const/let

#### Test Infrastructure (2/2 Complete)
- `test/shared/test-server.js` - Modern const/let, arrow functions
- `test/shared/start-echo-server.js` - Modern const/let, function expressions

#### Test Scripts (8/8 Complete)
- `test/scripts/memoryleak-server.js` - Modern const/let, arrow functions
- `test/scripts/memoryleak-client.js` - Modern const/let, arrow functions
- `test/scripts/libwebsockets-test-server.js` - Modern const/let, arrow functions
- `test/scripts/libwebsockets-test-client.js` - Modern const/let, arrow functions
- `test/scripts/fragmentation-test-client.js` - Modern const/let, arrow functions
- `test/scripts/fragmentation-test-server.js` - Modern const/let, arrow functions
- `test/scripts/echo-server.js` - Modern const/let, arrow functions
- `test/scripts/autobahn-test-client.js` - Modern const/let, arrow functions

### 3. **Example Files** (1 file)
**Priority: Low** - Examples should demonstrate modern patterns
Expand Down Expand Up @@ -91,12 +91,12 @@ The ES6 refactoring is **partially complete**. The following core library files

## Implementation Strategy

### Phase 1: Test Suite Modernization
### Phase 1: Test Suite Modernization ✅ **COMPLETED**
**Goal**: Ensure test reliability during refactoring
1. Refactor unit tests (`test/unit/*.js`)
2. Refactor test infrastructure (`test/shared/*.js`)
3. Refactor test scripts (`test/scripts/*.js`)
4. Run full test suite to ensure no regressions
1. Refactor unit tests (`test/unit/*.js`) - 5/5 files complete
2. Refactor test infrastructure (`test/shared/*.js`) - 2/2 files complete
3. Refactor test scripts (`test/scripts/*.js`) - 8/8 files complete
4. Run full test suite to ensure no regressions

### Phase 2: Code Quality Enhancements
**Goal**: Maximize modern JavaScript usage in core library
Expand Down
4 changes: 2 additions & 2 deletions docs/WebSocketServer.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ If true, the server will automatically send a ping to all clients every `keepali
**keepaliveInterval** - uint - *Default: 20000*
The interval in milliseconds to send keepalive pings to connected clients.

**dropConnectionOnKeepaliveTimeout** - boolean - *Default: true*
If true, the server will consider any connection that has not received any data within the amount of time specified by `keepaliveGracePeriod` after a keepalive ping has been sent. Ignored if `keepalive` is false.
**dropConnectionOnKeepaliveTimeout** - boolean - *Default: true*
If true, the server will consider any connection that has not received any data within the amount of time specified by `keepaliveGracePeriod` after a keepalive ping has been sent to be dead and will drop the connection once that grace period has elapsed without any incoming data. Ignored if `keepalive` is false.

**keepaliveGracePeriod** - uint - *Default: 10000*
The amount of time to wait after sending a keepalive ping before closing the connection if the connected peer does not respond. Ignored if `keepalive` or `dropConnectionOnKeepaliveTimeout` are false. The grace period timer is reset when any data is received from the client.
Expand Down
18 changes: 9 additions & 9 deletions example/whiteboard/public/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Whiteboard.prototype.clear = function() {

Whiteboard.prototype.handleMouseDown = function(event) {
this.mouseDown = true;
this.lastPoint = this.resolveMousePosition(event);
this.lastPoint = this.resolveMousePosition(event);
};

Whiteboard.prototype.handleMouseUp = function(event) {
Expand Down Expand Up @@ -178,12 +178,12 @@ Whiteboard.prototype.addCanvasEventListeners = function() {

Whiteboard.prototype.resolveMousePosition = function(event) {
var x, y;
if (event.offsetX) {
x = event.offsetX;
y = event.offsetY;
} else {
x = event.layerX - this.offsetX;
y = event.layerY - this.offsetY;
}
return { x: x, y: y };
if (event.offsetX) {
x = event.offsetX;
y = event.offsetY;
} else {
x = event.layerX - this.offsetX;
y = event.layerY - this.offsetY;
}
return { x: x, y: y };
};
6 changes: 4 additions & 2 deletions lib/W3CWebSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,12 @@ function createMessageEvent(data) {
function onConnect(connection) {
const self = this;

const { protocol, extensions } = connection;

this._readyState = OPEN;
this._connection = connection;
this._protocol = connection.protocol;
this._extensions = connection.extensions;
this._protocol = protocol;
this._extensions = extensions;

this._connection.on('close', function(code, reason) {
onClose.call(self, code, reason);
Expand Down
2 changes: 1 addition & 1 deletion lib/WebSocketClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ WebSocketClient.prototype.connect = function(requestUrl, protocols, origin, head

// validate protocol characters:
this.protocols.forEach((protocol) => {
for (var i=0; i < protocol.length; i ++) {
for (let i = 0; i < protocol.length; i++) {
const charCode = protocol.charCodeAt(i);
const character = protocol.charAt(i);
if (charCode < 0x0021 || charCode > 0x007E || protocolSeparators.indexOf(character) !== -1) {
Expand Down
18 changes: 9 additions & 9 deletions lib/WebSocketConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class WebSocketConnection extends EventEmitter {

if (this.config.dropConnectionOnKeepaliveTimeout) {
if (typeof(this.config.keepaliveGracePeriod) !== 'number') {
throw new Error('keepaliveGracePeriod must be specified and numeric if dropConnectionOnKeepaliveTimeout is true.');
throw new Error('keepaliveGracePeriod must be specified and numeric if dropConnectionOnKeepaliveTimeout is true.');
}
this._gracePeriodTimerHandler = this.handleGracePeriodTimer.bind(this);
}
Expand Down Expand Up @@ -256,14 +256,14 @@ class WebSocketConnection extends EventEmitter {
// Something bad happened.. get rid of this client.
this._debug('-- protocol error');
process.nextTick(() => {
self.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR, frame.dropReason);
this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR, frame.dropReason);
});
return;
}
else if (frame.frameTooLarge) {
this._debug('-- frame too large');
process.nextTick(() => {
self.drop(WebSocketConnection.CLOSE_REASON_MESSAGE_TOO_BIG, frame.dropReason);
this.drop(WebSocketConnection.CLOSE_REASON_MESSAGE_TOO_BIG, frame.dropReason);
});
return;
}
Expand All @@ -272,18 +272,18 @@ class WebSocketConnection extends EventEmitter {
if (frame.rsv1 || frame.rsv2 || frame.rsv3) {
this._debug('-- illegal rsv flag');
process.nextTick(() => {
self.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
'Unsupported usage of rsv bits without negotiated extension.');
});
return;
}

if (!this.assembleFragments) {
this._debug('-- emitting frame');
process.nextTick(() => { self.emit('frame', frame); });
process.nextTick(function() { self.emit('frame', frame); });
}

process.nextTick(() => { self.processFrame(frame); });
process.nextTick(function() { self.processFrame(frame); });

this.currentFrame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);

Expand Down Expand Up @@ -550,7 +550,7 @@ class WebSocketConnection extends EventEmitter {
// for text frames after combining all the fragments.
var bytesCopied = 0;
var binaryPayload = bufferAllocUnsafe(this.fragmentationSize);
var opcode = this.frameQueue[0].opcode;
const { opcode } = this.frameQueue[0];
this.frameQueue.forEach((currentFrame) => {
currentFrame.binaryPayload.copy(binaryPayload, bytesCopied);
bytesCopied += currentFrame.binaryPayload.length;
Expand Down Expand Up @@ -591,8 +591,8 @@ class WebSocketConnection extends EventEmitter {
// logic to emit the ping frame: this is only done when a listener is known to exist
// Expose a function allowing the user to override the default ping() behavior
var cancelled = false;
var cancel = () => {
cancelled = true;
const cancel = () => {
cancelled = true;
};
this.emit('ping', cancel, frame.binaryPayload);

Expand Down
9 changes: 4 additions & 5 deletions lib/WebSocketFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ WebSocketFrame.prototype.addData = function(bufferList) {
if (bufferList.length >= 2) {
bufferList.joinInto(this.frameHeader, 0, 0, 2);
bufferList.advance(2);
const firstByte = this.frameHeader[0];
const secondByte = this.frameHeader[1];
const [firstByte, secondByte] = this.frameHeader;

this.fin = Boolean(firstByte & 0x80);
this.rsv1 = Boolean(firstByte & 0x40);
Expand Down Expand Up @@ -94,17 +93,17 @@ WebSocketFrame.prototype.addData = function(bufferList) {
if (bufferList.length >= 8) {
bufferList.joinInto(this.frameHeader, 2, 0, 8);
bufferList.advance(8);
var lengthPair = [
const [highBits, lowBits] = [
this.frameHeader.readUInt32BE(2),
this.frameHeader.readUInt32BE(2+4)
];

if (lengthPair[0] !== 0) {
if (highBits !== 0) {
this.protocolError = true;
this.dropReason = 'Unsupported 64-bit length frame received';
return true;
}
this.length = lengthPair[1];
this.length = lowBits;
this.parseState = WAITING_FOR_MASK_KEY;
}
}
Expand Down
Loading