Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.

Feature fix todo refactor decode#28

Merged
NeatGuyCoding merged 4 commits intomainfrom
feature-fix-todo-refactor-decode
Oct 16, 2025
Merged

Feature fix todo refactor decode#28
NeatGuyCoding merged 4 commits intomainfrom
feature-fix-todo-refactor-decode

Conversation

@NeatGuyCoding
Copy link
Owner

@NeatGuyCoding NeatGuyCoding commented Oct 16, 2025

Summary by CodeRabbit

  • API Changes

    • Narrowed exception declared by JSON preprocessing (adjusts error type reported to callers).
  • Refactor

    • Packet decoding and body parsing reorganized for clearer, ByteBuf-focused processing and reduced inlined logic.
  • Tests

    • Test visibility reduced to package-private and new internal parsing/performance tests added.
  • Chores

    • Minor numeric parsing and attachment initialization cleanups; no behavioral changes for end users.

Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Copilot AI review requested due to automatic review settings October 16, 2025 07:38
@coderabbitai
Copy link

coderabbitai bot commented Oct 16, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactored packet decoding: extracted header/frame helpers and rewrote parseBody into dedicated packet-type handlers. preprocessJson(Integer, ByteBuf) now declares throws UnsupportedEncodingException. Many test classes/methods changed visibility from public to package-private.

Changes

Cohort / File(s) Summary
Core decoder refactoring
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java
Extracted decoding flow into decodeWithStringHeader, decodeWithLengthHeader, and decodeFrame. Rewrote parseBody to dispatch to parseConnectDisconnectBody, parseAckBody, parseEventBody, and handleBinaryAttachments. Numeric parsing simplified (removed redundant casts). preprocessJson(Integer, ByteBuf) signature changed to throws UnsupportedEncodingException. Minor attachment/list and TODO adjustments.
Test visibility changes
netty-socketio-core/src/test/java/com/corundumstudio/socketio/protocol/PacketDecoderTest.java
Changed test class and ~25 test methods from public to package-private. Added package-private tests for parseBody optimizations (CONNECT/DISCONNECT/EVENT) and a decode performance test. Some setup/teardown annotations adjusted.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PacketDecoder
    participant DecoderHelpers
    participant BodyParser

    Client->>PacketDecoder: decodePackets(ByteBuf)
    activate PacketDecoder

    alt String-header
        PacketDecoder->>DecoderHelpers: decodeWithStringHeader(buf, head)
        activate DecoderHelpers
        DecoderHelpers->>DecoderHelpers: decodeFrame(buf, head, len)
        DecoderHelpers->>BodyParser: parseBody(buf, packet)
        deactivate DecoderHelpers
    else Length-header
        PacketDecoder->>DecoderHelpers: decodeWithLengthHeader(buf, head)
        activate DecoderHelpers
        DecoderHelpers->>DecoderHelpers: decodeFrame(buf, head, len)
        DecoderHelpers->>BodyParser: parseBody(buf, packet)
        deactivate DecoderHelpers
    end

    alt MESSAGE packets
        BodyParser->>BodyParser: switch(subType)
        BodyParser->>BodyParser: parseConnectDisconnectBody()  -- CONNECT/DISCONNECT
        BodyParser->>BodyParser: parseAckBody()                 -- ACK/BINARY_ACK
        BodyParser->>BodyParser: parseEventBody()               -- EVENT/BINARY_EVENT
        BodyParser->>BodyParser: handleBinaryAttachments()      -- binary handling
    else Non-MESSAGE
        BodyParser->>BodyParser: early return
    end

    PacketDecoder->>Client: return decoded packets
    deactivate PacketDecoder
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I nibble bytes and hop through streams,
New helpers stitch the packet seams.
JSON trimmed to one exception's call,
Tests tuck in—still brave and tall.
Hooray, the decoder leaps with gleams!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "Feature fix todo refactor decode" is vague and generic, using non-descriptive terms that do not clearly convey the actual content of the changeset. While the title is technically related to the pull request (which does involve refactoring packet decoding logic), it fails to communicate the specific, meaningful changes: the major refactoring of the parseBody method with new dedicated parsing methods, the API change to preprocessJson's exception signature, visibility changes in test classes, and the extraction of helper methods. The phrase lacks clarity and specificity, making it difficult for a teammate scanning commit history to understand the primary intent or scope of the changes. Consider revising the title to be more specific and descriptive. For example, "Refactor PacketDecoder.parseBody with dedicated parsing methods and update preprocessJson exception signature" or "Extract parseBody parsing methods and narrow preprocessJson exception type" would better capture the primary changes. Avoid generic terms like "feature", "fix", "todo", and "refactor" without qualification, and ensure the title clearly indicates what is being changed and why.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-fix-todo-refactor-decode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the Socket.IO packet decoder to improve code organization and performance. The refactoring breaks down complex methods into smaller, focused functions and updates corresponding test methods.

  • Refactored decodePackets method to extract string header and length header decoding logic
  • Reorganized parseBody method using switch statement and extracted specialized parsing methods
  • Updated test method visibility from public to package-private and removed unnecessary IOException throws

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
PacketDecoder.java Refactored packet decoding logic with extracted methods and optimized code structure
PacketDecoderTest.java Updated test method visibility, removed unnecessary exception declarations, and added new test methods

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


@Test
public void testDecodeInvalidPacketType() throws IOException {
void testDecodeInvalidPacketType() {
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

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

These test methods removed IOException from their signatures but still call decoder.decodePackets() which throws IOException. The tests should either handle the exception or keep it in the method signature.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (2)

167-173: Consider reverting the condition formatting.

The multi-line formatting of this simple boolean condition reduces readability compared to the single-line version. The condition is straightforward and doesn't benefit from line breaks.

Apply this diff to restore the single-line format:

-        if (
-                lastPacket != null
-                && lastPacket.hasAttachments()
-                && !lastPacket.isAttachmentsLoaded()
-        ) {
+        if (lastPacket != null && lastPacket.hasAttachments() && !lastPacket.isAttachmentsLoaded()) {
                 return addAttachment(head, frame, lastPacket);
             }

226-226: TODO comment is outdated.

The TODO mentions optimizing by working directly with ByteBuf, but this refactoring didn't implement that optimization. Either implement the optimization or remove the stale comment.

netty-socketio-core/src/test/java/com/corundumstudio/socketio/protocol/PacketDecoderTest.java (1)

669-698: Performance test has an overly optimistic threshold.

The test expects decoding to complete in under 100ms, but this threshold may be too lenient for detecting regressions. Consider:

  1. The test creates a large JSON string (1000+ elements) but mocks the JSON parsing, so it doesn't exercise the actual parsing path
  2. A 100ms threshold is very generous for an operation that should complete in single-digit milliseconds

Consider tightening the threshold and removing the mock to test actual performance:

-        // Should complete within reasonable time (less than 100ms)
-        assertTrue((endTime - startTime) < 100, 
-                  "Decoding took too long: " + (endTime - startTime) + "ms");
+        // Should complete within reasonable time (less than 10ms)
+        assertTrue((endTime - startTime) < 10, 
+                  "Decoding took too long: " + (endTime - startTime) + "ms");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a94d77c and cd55e24.

📒 Files selected for processing (2)
  • netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (8 hunks)
  • netty-socketio-core/src/test/java/com/corundumstudio/socketio/protocol/PacketDecoderTest.java (31 hunks)
🔇 Additional comments (12)
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (8)

76-76: LGTM! Simplified bit masking operation.

Removed unnecessary cast while preserving the logic to extract the low nibble. The & 0xF operation already promotes the byte to int.


87-87: LGTM! Removed redundant casts.

The explicit (int) cast before buffer.readByte() is unnecessary since the bitwise AND operation already promotes the result to int.

Also applies to: 92-92


118-151: Well-structured extraction of decoding logic.

The refactoring successfully separates concerns by extracting decodeWithStringHeader, decodeWithLengthHeader, and decodeFrame methods. The shared decodeFrame helper eliminates duplication and improves maintainability.


317-325: LGTM! Clean separation of CONNECT/DISCONNECT handling.

The extracted method correctly handles both packet types, with auth data parsing only for CONNECT packets when data is present.


330-340: LGTM! ACK handling logic preserved correctly.

The extracted method maintains the original behavior: reads ACK args when a callback exists, otherwise clears the frame buffer.


345-350: LGTM! EVENT parsing extracted cleanly.

The method focuses solely on parsing EVENT packet bodies and setting the event name and arguments.


355-361: LGTM! Binary attachment handling isolated correctly.

The method only performs actions when attachments exist and aren't fully loaded, making it safe to call from the default case.


282-312: Code is correct—all packet subtypes are handled safely.

Verification confirms that all PacketType MESSAGE subtypes (CONNECT, DISCONNECT, EVENT, ACK, ERROR, BINARY_EVENT, BINARY_ACK) are handled correctly. The six explicitly cased subtypes are processed with their respective handlers, and ERROR (along with any future subtypes) correctly fall through to the default case. The handleBinaryAttachments method only acts when packet.hasAttachments() && !packet.isAttachmentsLoaded() is true, so packets without pending attachments exit harmlessly. The behavior is sound and requires no changes.

netty-socketio-core/src/test/java/com/corundumstudio/socketio/protocol/PacketDecoderTest.java (4)

53-53: Visibility change to package-private is appropriate.

The test class visibility change from public to package-private aligns with testing internal implementation details rather than public API contracts.


71-72: Good practice: Added @OverRide annotations.

Adding @Override annotations to setup and teardown methods improves compile-time safety and documents the inheritance relationship with BaseProtocolTest.

Also applies to: 82-83


489-531: Test signatures updated to match preprocessJson change.

The test methods no longer declare throws IOException, consistent with the API change in preprocessJson. The tests correctly verify JSONP preprocessing functionality.


610-663: Good coverage for refactored parseBody methods.

The new tests verify the optimized parseBody switch statement for CONNECT, DISCONNECT, and EVENT packets. This provides confidence that the refactoring preserved the original behavior.

Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (1)

53-56: Internalize UnsupportedEncodingException; avoid checked throws from public API

UTF‑8 is always present on the JVM; prefer catching UnsupportedEncodingException and rethrowing IllegalStateException to keep callers free of checked exceptions. Also: nice fix using the Java 8‑compatible URLDecoder overload.

-    public ByteBuf preprocessJson(Integer jsonIndex, ByteBuf content) throws UnsupportedEncodingException {
-        String packet = URLDecoder.decode(content.toString(CharsetUtil.UTF_8), CharsetUtil.UTF_8.name());
+    public ByteBuf preprocessJson(Integer jsonIndex, ByteBuf content) {
+        final String raw = content.toString(CharsetUtil.UTF_8);
+        final String packet;
+        try {
+            packet = URLDecoder.decode(raw, CharsetUtil.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            throw new IllegalStateException("UTF-8 encoding not supported", e);
+        }
🧹 Nitpick comments (1)
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (1)

148-152: Add bounds check before slicing frame

Defensively ensure there are enough bytes for the announced frame to avoid IndexOutOfBoundsException and produce a clearer error.

-        ByteBuf frame = buffer.slice(buffer.readerIndex() + 1, len);
-        buffer.readerIndex(buffer.readerIndex() + 1 + len);
+        if (buffer.readableBytes() < 1 + len) {
+            throw new IOException("Incomplete frame: expected " + len + " bytes, available " + Math.max(buffer.readableBytes() - 1, 0));
+        }
+        ByteBuf frame = buffer.slice(buffer.readerIndex() + 1, len);
+        buffer.readerIndex(buffer.readerIndex() + 1 + len);
         return decode(client, frame);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd55e24 and 401673b.

📒 Files selected for processing (1)
  • netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (9 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (21) / build
  • GitHub Check: build (17) / build
🔇 Additional comments (8)
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java (8)

77-77: LGTM: simpler digit extraction

Masking with 0xF is correct for ASCII digits and avoids the extra cast.


88-95: LGTM: safe nibble extraction for type ids

Using readByte() & 0xF ensures correct unsigned value for packet/inner types.


110-117: Good split into header-specific decode paths

Cleaner control flow; keeps decodePackets focused and readable.


283-313: LGTM: body parsing split improves clarity and testability

Early return for non‑MESSAGE and subtype‑specific handlers make the flow easier to reason about.


318-326: LGTM: CONNECT/DISCONNECT parsing

Namespace first, optional CONNECT auth JSON handled via ByteBufInputStream.


331-341: LGTM: ACK parsing with callback lookup

Gracefully ignores payload when callback is absent.


346-351: LGTM: EVENT parsing

Reads Event and sets name/args as expected.


356-361: LGTM: binary attachment deferral

Persists source and defers to subsequent frames when attachments pending.

Comment on lines +124 to +131
int maxLength = Math.min(buffer.readableBytes(), 10);
int headEndIndex = buffer.bytesBefore(maxLength, (byte) -1);
if (headEndIndex == -1) {
headEndIndex = buffer.bytesBefore(maxLength, (byte) 0x3f);
}
int len = (int) readLong(buffer, headEndIndex);
return decodeFrame(buffer, client, len);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard against missing length delimiter to prevent readerIndex corruption

If neither 0xFF nor '?' is found within maxLength, headEndIndex stays -1 and readLong(-1) will move readerIndex backwards, corrupting state. Add a check before calling readLong.

-        int headEndIndex = buffer.bytesBefore(maxLength, (byte) -1);
-        if (headEndIndex == -1) {
-            headEndIndex = buffer.bytesBefore(maxLength, (byte) 0x3f);
-        }
-        int len = (int) readLong(buffer, headEndIndex);
+        int headEndIndex = buffer.bytesBefore(maxLength, (byte) -1); // 0xFF
+        if (headEndIndex == -1) {
+            headEndIndex = buffer.bytesBefore(maxLength, (byte) 0x3f); // '?'
+        }
+        if (headEndIndex < 0) {
+            throw new IOException("Invalid string header: missing length delimiter within " + maxLength + " bytes");
+        }
+        int len = (int) readLong(buffer, headEndIndex);
         return decodeFrame(buffer, client, len);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int maxLength = Math.min(buffer.readableBytes(), 10);
int headEndIndex = buffer.bytesBefore(maxLength, (byte) -1);
if (headEndIndex == -1) {
headEndIndex = buffer.bytesBefore(maxLength, (byte) 0x3f);
}
int len = (int) readLong(buffer, headEndIndex);
return decodeFrame(buffer, client, len);
}
int maxLength = Math.min(buffer.readableBytes(), 10);
int headEndIndex = buffer.bytesBefore(maxLength, (byte) -1); // 0xFF
if (headEndIndex == -1) {
headEndIndex = buffer.bytesBefore(maxLength, (byte) 0x3f); // '?'
}
if (headEndIndex < 0) {
throw new IOException("Invalid string header: missing length delimiter within " + maxLength + " bytes");
}
int len = (int) readLong(buffer, headEndIndex);
return decodeFrame(buffer, client, len);
}
🤖 Prompt for AI Agents
In
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java
around lines 124 to 131, the code calls readLong(buffer, headEndIndex) even when
headEndIndex is -1 which can move readerIndex backwards and corrupt state; add a
guard: after computing headEndIndex check if it is -1 and handle that case (for
example throw a clear DecoderException/IllegalStateException or return an
error/empty result) instead of calling readLong; ensure the chosen handling
leaves the buffer readerIndex unchanged and provides a clear error message about
a missing length delimiter.

Comment on lines +137 to +142
private Packet decodeWithLengthHeader(ByteBuf buffer, ClientHead client) throws IOException {
int lengthEndIndex = buffer.bytesBefore((byte) ':');
int lenHeader = (int) readLong(buffer, lengthEndIndex);
int len = utf8scanner.getActualLength(buffer, lenHeader);
return decodeFrame(buffer, client, len);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Validate presence of ':' in length header

bytesBefore may return -1; passing that to readLong has the same corruption risk. Guard it.

-        int lengthEndIndex = buffer.bytesBefore((byte) ':');
-        int lenHeader = (int) readLong(buffer, lengthEndIndex);
+        int lengthEndIndex = buffer.bytesBefore((byte) ':');
+        if (lengthEndIndex < 0) {
+            throw new IOException("Invalid length header: ':' not found");
+        }
+        int lenHeader = (int) readLong(buffer, lengthEndIndex);
         int len = utf8scanner.getActualLength(buffer, lenHeader);
         return decodeFrame(buffer, client, len);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private Packet decodeWithLengthHeader(ByteBuf buffer, ClientHead client) throws IOException {
int lengthEndIndex = buffer.bytesBefore((byte) ':');
int lenHeader = (int) readLong(buffer, lengthEndIndex);
int len = utf8scanner.getActualLength(buffer, lenHeader);
return decodeFrame(buffer, client, len);
}
private Packet decodeWithLengthHeader(ByteBuf buffer, ClientHead client) throws IOException {
int lengthEndIndex = buffer.bytesBefore((byte) ':');
if (lengthEndIndex < 0) {
throw new IOException("Invalid length header: ':' not found");
}
int lenHeader = (int) readLong(buffer, lengthEndIndex);
int len = utf8scanner.getActualLength(buffer, lenHeader);
return decodeFrame(buffer, client, len);
}
🤖 Prompt for AI Agents
In
netty-socketio-core/src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java
around lines 137 to 142, guard against bytesBefore returning -1 before passing
it to readLong: if lengthEndIndex == -1 (no ':' found) throw an IOException (or
appropriate protocol/parsing exception) with a clear message about a missing
length header delimiter, thereby avoiding passing -1 into readLong and
preventing buffer corruption; after this check continue to parse lenHeader,
compute actual length and call decodeFrame as before.

@NeatGuyCoding NeatGuyCoding merged commit 79c542a into main Oct 16, 2025
3 checks passed
@NeatGuyCoding NeatGuyCoding deleted the feature-fix-todo-refactor-decode branch October 16, 2025 08:57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants