Skip to content

feat: add SSE port, protocol config, and port mapping support#3973

Open
officialasishkumar wants to merge 6 commits intofix/record-long-lived-connfrom
feat/sse-port
Open

feat: add SSE port, protocol config, and port mapping support#3973
officialasishkumar wants to merge 6 commits intofix/record-long-lived-connfrom
feat/sse-port

Conversation

@officialasishkumar
Copy link
Copy Markdown
Member

@officialasishkumar officialasishkumar commented Mar 26, 2026

Describe the changes that are made

This pull request introduces enhanced support for protocol-specific port configuration and flexible port replacement logic, especially for Server-Sent Events (SSE) and other protocols. The changes allow users to specify ports at both the top-level and per-protocol level, and to override ports dynamically via configuration. The port replacement mechanism is now more robust, supporting both URL-based and direct port mappings, with a clear precedence order. Additionally, utility functions and configuration structures have been updated to support these features.

Protocol and Port Configuration Enhancements:

  • Added new SSEPort fields to the Test and ReRecord structs in config/config.go, and introduced a ProtocolConfig map for per-protocol port settings. The default configuration in config/default.go now includes protocol-level ports for HTTP, SSE, and gRPC. [1] [2] [3]
  • Updated CLI flag handling in cli/provider/cmd.go to support the new --sse-port flag, including normalization and validation logic. [1] [2] [3]

Flexible Port Replacement Logic:

  • Refactored the ReplaceWith configuration to support both URL and port mappings, and implemented a mergeReplaceWith helper in pkg/service/replay/hooks.go to merge global and per-test-set replacements for both URLs and ports. [1] [2] [3]
  • The SimulationConfig struct in pkg/util.go now includes a PortMappings field, and all simulation and request preparation logic has been updated to use both URL replacements and port mappings. [1] [2] [3]

Precedence and Port Resolution Improvements:

  • The ResolveTestTarget function in pkg/util.go has been overhauled to implement a clear precedence order: AppPort → ConfigPort (including protocol-specific ports) → URL replacements (with explicit port) → port mappings (highest priority). This ensures that the most specific port configuration is always used. [1] [2] [3] [4] [5] [6]

SSE Request Detection and Handling:

  • Added an IsSSERequest utility function to pkg/util.go to detect SSE requests based on headers, and updated replay/orchestrator logic to use the correct port for SSE test cases. [1] [2]

Test and Maintenance Updates:

  • Updated tests in pkg/util_test.go to reflect the new ResolveTestTarget signature and logic. [1] [2]

These changes provide much greater flexibility and correctness in how ports are configured and overridden for different protocols and test scenarios.

Links & References

Closes: #3974

  • NA (if very small change like typo, linting, etc.)

🔗 Related PRs

  • NA

🐞 Related Issues

  • NA

📄 Related Documents

  • NA

What type of PR is this? (check all applicable)

  • 📦 Chore
  • 🍕 Feature
  • 🐞 Bug Fix
  • 📝 Documentation Update
  • 🎨 Style
  • 🧑‍💻 Code Refactor
  • 🔥 Performance Improvements
  • ✅ Test
  • 🔁 CI
  • ⏩ Revert

Added e2e test pipeline?

  • 👍 yes
  • 🙅 no, because they aren't needed
  • 🙋 no, because I need help

Added comments for hard-to-understand areas?

  • 👍 yes
  • 🙅 no, because the code is self-explanatory

Added to documentation?

  • 📜 README.md
  • 📓 Wiki
  • 🙅 no documentation needed

Are there any sample code or steps to test the changes?

  • 👍 yes, mentioned below
  • 🙅 no, because it is not needed

Self Review done?

  • ✅ yes
  • ❌ no, because I need help

Any relevant screenshots, recordings or logs?

  • NA

🧠 Semantics for PR Title & Branch Name

Please ensure your PR title and branch name follow the Keploy semantics:

📌 PR Semantics Guide
📌 Branch Semantics Guide

Examples:

  • PR Title: fix: patch MongoDB document update bug
  • Branch Name: feat/#1-login-flow (You may skip mentioning the issue number in the branch name if the change is small and the PR description clearly explains it.)

Additional checklist:

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot bot commented Mar 26, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental Review (84b3d247bf10bb)

The latest commit (7bf10bbb) contains only a documentation improvement:

  1. config/config.go - Clarified ProtocolConfig comment:
    • Previous: "New protocols can be added in the config file without code changes"
    • Updated: "The map schema allows additional protocol names in the config without schema changes, but only protocols recognized by the application are currently used by the replay and protocol-handling logic"
    • This is more accurate and sets correct expectations about protocol extensibility

All previous fixes remain in place from commit 84b3d24be:

Previous Issue Resolution
cli/provider/cmd.go:1128 - Error message used config field name instead of flag name ✅ Fixed
pkg/service/orchestrator/rerecord.go - Error log lacked actionable next step ✅ Fixed
pkg/util.go:891 - IsSSERequest duplicated detection logic ✅ Fixed
pkg/util.go:2891 - Info log should be Debug level ✅ Fixed
Files Reviewed (1 file in this increment)
  • config/config.go - 0 issues (documentation comment update only)

Reviewed by claude-4.5-opus-20251124 · 95,132 tokens

Copy link
Copy Markdown
Contributor

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

Adds protocol-aware port configuration (including SSE), plus a higher-priority port-mapping override mechanism, so replay/rerecord can more flexibly resolve the correct target host/port for different protocols and environments.

Changes:

  • Introduces ssePort and a test.protocol map for per-protocol port overrides in config (and defaults).
  • Extends replaceWith to support port-to-port mappings and wires this through replay simulation to ResolveTestTarget.
  • Adds SSE request detection and uses it to select the correct port during replay/rerecord.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
config/config.go Adds SSEPort, protocol map config, and replaceWith.port structure.
config/default.go Updates default config YAML to include ssePort and protocol port sections.
cli/provider/cmd.go Adds --sse-port flag and flag normalization/validation wiring.
pkg/service/replay/hooks.go Computes effective per-test-case config port (HTTP vs SSE vs gRPC) and merges URL replacements + port mappings.
pkg/util.go Adds PortMappings to simulation config, introduces IsSSERequest, and updates ResolveTestTarget precedence to include port mappings.
pkg/http2.go Passes port mappings through gRPC simulation target resolution.
pkg/service/orchestrator/rerecord.go Applies ReRecord.SSEPort when rerecording SSE testcases.
pkg/util_test.go Updates tests for ResolveTestTarget signature change.
Comments suppressed due to low confidence (3)

pkg/util.go:2812

  • replacementHasPort detection can be wrong when a replacement value is a full URL (or contains a path/query). After stripping only the scheme, hasExplicitPort is called on strings that may still include /..., causing explicit ports like http://localhost:3000/api to be treated as “no port” and then overridden by config/port mappings. Consider parsing replacement as a URL when isHTTP (or extracting the host portion before calling hasExplicitPort).
				// Check if the replacement value explicitly defines a port.
				checkStr := replacement
				if isHTTP {
					if strings.HasPrefix(replacement, "http://") {
						checkStr = strings.TrimPrefix(replacement, "http://")
					} else if strings.HasPrefix(replacement, "https://") {
						checkStr = strings.TrimPrefix(replacement, "https://")
					}
				}

				if hasExplicitPort(checkStr) {
					replacementHasPort = true
				}

pkg/util.go:2820

  • URL replacement selection is based on iterating over a map. If multiple urlReplacements keys can match the same target, the applied replacement becomes nondeterministic because Go map iteration order is randomized. Consider making the match deterministic (for example: sort keys, prefer longest match, or define an explicit priority order).
	// Step 1: Check replaceWith URL
	if len(urlReplacements) > 0 {
		for substr, replacement := range urlReplacements {
			if strings.Contains(finalTarget, substr) {
				replacementMatched = true
				finalTarget = strings.Replace(finalTarget, substr, replacement, 1)

				// Check if the replacement value explicitly defines a port.
				checkStr := replacement
				if isHTTP {
					if strings.HasPrefix(replacement, "http://") {
						checkStr = strings.TrimPrefix(replacement, "http://")
					} else if strings.HasPrefix(replacement, "https://") {
						checkStr = strings.TrimPrefix(replacement, "https://")
					}
				}

				if hasExplicitPort(checkStr) {
					replacementHasPort = true
				}

				logger.Debug("Applied replaceWith substitution",
					zap.String("find", substr),
					zap.String("replace", replacement),
					zap.String("result_target", finalTarget),
					zap.Bool("replacement_has_port", replacementHasPort))
				break
			}

pkg/util_test.go:1202

  • ResolveTestTarget now supports portMappings, but there are no test cases exercising the new precedence rules (URL replacement with explicit port + port mapping override, mapping-only override, etc.). Adding a few table-driven cases here would protect the new behavior and prevent regressions.

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := ResolveTestTarget(tt.originalTarget, tt.urlReplacements, nil, tt.configHost, tt.appPort, tt.configPort, tt.isHTTP, logger)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Equal(t, tt.expectedTarget, got)
			}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Introduce ssePort (top-level + CLI flag), extensible protocol-level
port overrides (map[string]ProtocolSettings), and port-to-port mapping
in replaceWith for fine-grained replay target resolution.

Priority (lowest → highest): appPort → port/grpcPort/ssePort →
protocol.<proto>.port → replaceWith URL → replaceWith port mapping.

Signed-off-by: Asish Kumar <[email protected]>
- Pass --sse-port 8047 to test SSE protocol routing
- Add port mapping test step: disables ping server (SKIP_PING_SERVER=1)
  and verifies replaceWith port mapping 8050→8000 works correctly

Signed-off-by: Asish Kumar <[email protected]>
@officialasishkumar officialasishkumar force-pushed the feat/sse-port branch 2 times, most recently from 167b81b to a163217 Compare March 26, 2026 13:21
@officialasishkumar officialasishkumar changed the base branch from fix/record-long-lived-conn to main March 26, 2026 13:21
@github-actions
Copy link
Copy Markdown

🚀 Keploy Performance Test Results

Multi-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.

Run P50 P90 P99 RPS Error Rate Status
1 2.96ms 5.73ms 509.04ms 99.09 0.00% ❌ FAIL
2 N/A N/A N/A N/A N/A ✅ PASS

Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1%

Result: PASSED - Only 1 out of 3 runs failed (threshold: 2)

P50, P90, and P99 percentiles naturally filter out outliers

Copy link
Copy Markdown
Contributor

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

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


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@officialasishkumar officialasishkumar changed the base branch from main to fix/record-long-lived-conn March 26, 2026 13:30
@github-actions
Copy link
Copy Markdown

🚀 Keploy Performance Test Results

Multi-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.

Run P50 P90 P99 RPS Error Rate Status
1 3.81ms 8.62ms 729.05ms 98.75 0.00% ❌ FAIL
2 3.11ms 4.08ms 5.6ms 100.00 0.00% ✅ PASS
3 2.97ms 3.89ms 5.36ms 100.01 0.00% ✅ PASS

Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1%

Result: PASSED - Only 1 out of 3 runs failed (threshold: 2)

P50, P90, and P99 percentiles naturally filter out outliers

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <[email protected]>
Signed-off-by: Asish Kumar <[email protected]>
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.

3 participants