Skip to content

Fix: Add buffer accumulation to TDSTLSFramer for Azure SQL Database#2

Open
mklahn wants to merge 1 commit into
vkuttyp:mainfrom
mklahn:fix/azure-sql-tds-buffer-fragmentation
Open

Fix: Add buffer accumulation to TDSTLSFramer for Azure SQL Database#2
mklahn wants to merge 1 commit into
vkuttyp:mainfrom
mklahn:fix/azure-sql-tds-buffer-fragmentation

Conversation

@mklahn
Copy link
Copy Markdown

@mklahn mklahn commented May 28, 2026

  • Add accumulator buffer to handle fragmented TCP packets
  • Fix connection reset errors with Azure SQL Database
  • Change TDS version from 8.0 to 7.4 for compatibility

Fixes connection failures when TLS ServerHello exceeds TCP segment size. Tested against Azure SQL Database with successful queries.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Azure SQL compatibility by adjusting database connection protocol version.
    • Enhanced TLS handshake reliability by properly handling incomplete packets across multiple network reads.

Review Change Stack

- Add accumulator buffer to handle fragmented TCP packets
- Fix connection reset errors with Azure SQL Database
- Change TDS version from 8.0 to 7.4 for compatibility

Fixes connection failures when TLS ServerHello exceeds TCP segment size.
Tested against Azure SQL Database with successful queries.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

TDS Pre-Login handshake handling is improved by downgrading the client version from 8.0 to 7.4 for better Azure SQL compatibility, and the TLS framer now buffers incomplete packets across multiple channel reads instead of immediately stopping on partial frames.

Changes

TDS Pre-Login and TLS Framer Improvements

Layer / File(s) Summary
TDS 7.4 client version for Azure compatibility
Sources/CosmoMSSQL/TDS/TDSPreLogin.swift
TDSPreLoginRequest.clientVersion default changed from (8, 0, 0, 0) to (7, 4, 0, 0) with an added comment indicating the change improves Azure SQL compatibility.
Partial packet buffering in TLS framer
Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift
TDSTLSFramer adds an accumulatedBuffer field to retain incomplete TDS Pre-Login data between reads. The channelRead method now appends incoming bytes, repeatedly parses and forwards complete TDS Pre-Login packets (stripping the 8-byte TDS header), and stores remaining incomplete bytes for the next read cycle.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

Poem

🐰 A rabbit hops through packets small,
Now buffering bytes, not dropping them all—
From eight to seven-four we dance,
Azure SQL gets a better chance! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding buffer accumulation to TDSTLSFramer for Azure SQL compatibility, which aligns with the primary fix described in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift`:
- Around line 57-65: The guard that checks the TDS packet length (reading
lengthBE in TDSTLSFramer.swift) currently treats all failures as a partial read
by saving accumulatedBuffer; change it so that a lengthBE < 8 is treated as a
protocol error rather than buffering: if lengthBE < 8, do not assign
accumulatedBuffer, instead fail the pipeline/connection (emit/throw a protocol
error indicating "invalid TDS header length") and close the connection; only
keep the existing buffering logic for cases where Int(lengthBE) >
buf.readableBytes (genuine partial frames). Ensure you reference the same
variables (lengthBE, accumulatedBuffer, buf) and perform a clear error
emission/connection teardown in the TDSTLSFramer code path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c15cd75d-6490-449d-9682-432f12857138

📥 Commits

Reviewing files that changed from the base of the PR and between 00f5f3e and 9a340b2.

📒 Files selected for processing (2)
  • Sources/CosmoMSSQL/TDS/TDSPreLogin.swift
  • Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift

Comment on lines 57 to +65
guard
let lengthBE: UInt16 = buf.getInteger(at: buf.readerIndex + 2, endianness: .big),
Int(lengthBE) <= buf.readableBytes,
Int(lengthBE) >= 8
else { break }
else {
// Save remaining data for next read
accumulatedBuffer = buf
return
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle invalid TDS header lengths as protocol errors, not partial frames.

Int(lengthBE) >= 8 is currently grouped with partial-read handling. For malformed headers (lengthBE < 8), this path keeps buffering forever and never advances, which can stall the connection and grow memory.

Suggested fix
         while buf.readableBytes >= 8 {
-            guard
-                let lengthBE: UInt16 = buf.getInteger(at: buf.readerIndex + 2, endianness: .big),
-                Int(lengthBE) <= buf.readableBytes,
-                Int(lengthBE) >= 8
-            else {
-                // Save remaining data for next read
-                accumulatedBuffer = buf
-                return
-            }
-            let packetLen = Int(lengthBE)
+            guard let lengthBE: UInt16 = buf.getInteger(at: buf.readerIndex + 2, endianness: .big) else {
+                accumulatedBuffer = buf
+                return
+            }
+            let packetLen = Int(lengthBE)
+
+            // Invalid TDS frame length: fail fast instead of buffering forever.
+            guard packetLen >= 8 else {
+                accumulatedBuffer = nil
+                context.close(promise: nil)
+                return
+            }
+
+            // Incomplete frame: keep buffering until full packet arrives.
+            guard packetLen <= buf.readableBytes else {
+                accumulatedBuffer = buf
+                return
+            }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift` around lines 57 - 65, The guard
that checks the TDS packet length (reading lengthBE in TDSTLSFramer.swift)
currently treats all failures as a partial read by saving accumulatedBuffer;
change it so that a lengthBE < 8 is treated as a protocol error rather than
buffering: if lengthBE < 8, do not assign accumulatedBuffer, instead fail the
pipeline/connection (emit/throw a protocol error indicating "invalid TDS header
length") and close the connection; only keep the existing buffering logic for
cases where Int(lengthBE) > buf.readableBytes (genuine partial frames). Ensure
you reference the same variables (lengthBE, accumulatedBuffer, buf) and perform
a clear error emission/connection teardown in the TDSTLSFramer code path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant