Skip to content

fix SSEOption #63

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

Merged
merged 5 commits into from
Mar 24, 2025
Merged

fix SSEOption #63

merged 5 commits into from
Mar 24, 2025

Conversation

a67793581
Copy link
Contributor

@a67793581 a67793581 commented Mar 21, 2025

SSEOption should not have negative effects when used repeatedly but should always remain consistent.

Summary by CodeRabbit

  • New Features

    • Enhanced service endpoint configuration with improved URL validation and consistent path construction to prevent misconfigurations and ensure reliable service behavior.
  • Tests

    • Added a new test case to ensure consistent behavior of the service configuration when instantiated multiple times with random options.
    • Updated existing test assertions for baseURL and HTTP server handling.

Copy link
Contributor

coderabbitai bot commented Mar 21, 2025

Walkthrough

This update enhances the validation logic for the baseURL in the WithBaseURL function of the SSEServer struct. It introduces checks for non-empty values, valid schemes, and proper formatting. The WithBasePath function has been modified to change how the base path is managed. New methods for constructing complete URLs and extracting paths have been added, improving code organization. Additionally, a new test case ensures consistent behavior of the SSEServer when instantiated multiple times with various options.

Changes

File(s) Change Summary
server/sse.go Enhanced URL validation in WithBaseURL (checking for non-empty host, trimming trailing slashes) and removed direct concatenation in WithBasePath. Added new methods: GetUrlPath, CompleteSseEndpoint, CompleteSsePath, CompleteMessageEndpoint, and CompleteMessagePath. Updated handleSSE and ServeHTTP methods to utilize these new helpers.
server/sse_test.go Added a new test case in TestSSEServer to verify that repeated use of configuration options maintains consistent SSEServer properties. Modified existing test assertions regarding baseURL and HTTP server handling.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • ezynda3

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 970aa8f and 147f550.

📒 Files selected for processing (1)
  • server/sse.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/sse.go

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 5

🧹 Nitpick comments (2)
server/sse.go (2)

59-71: Improved validation for baseURL.

The enhanced validation in WithBaseURL function ensures proper URL format, guards against empty hosts, and provides clear error messages. However, using panic might be too severe for configuration issues.

Consider returning an error instead of panicking to allow more graceful error handling:

-func WithBaseURL(baseURL string) SSEOption {
-	return func(s *SSEServer) {
-		if baseURL != "" {
-			u, err := url.Parse(baseURL)
-			if err != nil {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-			// Check if the host is empty or only contains a port
-			if u.Host == "" || strings.HasPrefix(u.Host, ":") {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-		}
-		s.baseURL = strings.TrimSuffix(baseURL, "/")
-	}
-}
+// WithBaseURL sets the base URL for the SSE server
+func WithBaseURL(baseURL string) SSEOption {
+	return func(s *SSEServer) {
+		if baseURL != "" {
+			u, err := url.Parse(baseURL)
+			if err != nil {
+				s.baseURL = ""
+				return
+			}
+			// Check if the host is empty or only contains a port
+			if u.Host == "" || strings.HasPrefix(u.Host, ":") {
+				s.baseURL = ""
+				return
+			}
+		}
+		s.baseURL = strings.TrimSuffix(baseURL, "/")
+	}
+}

8-8: Use time.Now().UnixNano() for better seed in tests.

While using math/rand is appropriate here, using a better seed would enhance test reliability.

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
-	"math/rand"
+	"math/rand"
	"net/http"
	"net/http/httptest"

// Later in the added test:
+		// Initialize random seed
+		rand.Seed(time.Now().UnixNano())
		for i := 0; i < 100; i++ {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 771de1d and 3579581.

📒 Files selected for processing (2)
  • server/sse.go (4 hunks)
  • server/sse_test.go (2 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
server/sse_test.go (2)
server/server.go (1) (1)
  • NewMCPServer (251-277)
server/sse.go (7) (7)
  • SSEOption (56-56)
  • WithBasePath (76-84)
  • WithBaseURL (59-73)
  • WithMessageEndpoint (87-91)
  • WithSSEEndpoint (94-98)
  • WithHTTPServer (101-105)
  • NewSSEServer (116-129)
server/sse.go (1)
server/server.go (16) (16)
  • s (100-105)
  • s (108-116)
  • s (119-123)
  • s (126-150)
  • s (153-179)
  • s (280-470)
  • s (473-486)
  • s (489-502)
  • s (505-513)
  • s (516-518)
  • s (521-536)
  • s (539-544)
  • s (547-559)
  • s (562-569)
  • s (571-623)
  • s (625-631)
🪛 GitHub Actions: go
server/sse_test.go

[error] 34-34: TestSSEServer/Can_instantiate failed: Expected baseURL http://localhost:8080/mcp, got http://localhost:8080


[error] 376-376: TestSSEServer/Works_with_custom_mux failed: Expected status 200, got 404


[error] 383-383: TestSSEServer/Works_with_custom_mux failed: Failed to read SSE response: EOF

🔇 Additional comments (4)
server/sse_test.go (1)

618-661: Good addition of tests to ensure SSEOption consistency.

This test verifies that SSEOption functions behave consistently when applied repeatedly in random order, which is a critical check given the PR objective of ensuring SSEOption doesn't have negative effects when used repeatedly.

The test is thorough in checking all configurable properties of the SSEServer, including basePath, baseURL, messageEndpoint, sseEndpoint, and HTTP server instance.

server/sse.go (3)

231-231: Nice simplification using the new CompleteMessageEndpoint method.

Using the CompleteMessageEndpoint method instead of manually constructing the URL makes the code more maintainable and consistent.


360-362: Good addition of helper method for constructing the complete SSE endpoint.

The CompleteSseEndpoint method provides a clean way to construct the full URL for the SSE endpoint, improving code consistency.


367-369: Good addition of helper method for constructing the complete message endpoint.

The CompleteMessageEndpoint method provides a clean way to construct the full URL for the message endpoint, improving code consistency.

server/sse.go Outdated
// Use exact path matching rather than Contains
if path == ssePath {
if path == s.CompleteSsePath() {
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix path comparison to handle URL parsing errors.

The path comparison will fail if CompleteSsePath encounters URL parsing errors, which is causing the pipeline failures.


🏁 Script executed:

#!/bin/bash
# Check the failing test cases to verify our hypothesis

# First, let's check what's happening in the "Can instantiate" test
echo "Checking the 'Can instantiate' test failure..."
grep -A 10 "Can instantiate" server/sse_test.go

# Now let's verify the path comparison in the ServeHTTP method
echo "Verifying the path comparison in ServeHTTP..."
cat server/sse.go | grep -A 20 "ServeHTTP"

Length of output: 963


Action Required: Update URL Path Comparison to Handle Parsing Errors

The current implementation in server/sse.go compares r.URL.Path directly against s.CompleteSsePath() without considering that URL parsing might fail. If CompleteSsePath encounters a parsing error, the resulting comparison will likely be invalid and lead to the pipeline failures observed.

Suggestions:

  • Error Handling: Modify the logic in ServeHTTP to capture and inspect any errors from CompleteSsePath(). If an error occurs, return a suitable HTTP error (e.g., a 500 Internal Server Error) rather than proceeding with a faulty comparison.
  • Consistency: Review similar comparisons (like the one for s.CompleteMessagePath()) to ensure they also handle potential URL parsing issues consistently.

Example Diff Snippet:

-	if path == s.CompleteSsePath() {
-		s.handleSSE(w, r)
-		return
-	}
+	pathSse, err := s.CompleteSsePath()
+	if err != nil {
+		http.Error(w, "Internal server error", http.StatusInternalServerError)
+		return
+	}
+	if path == pathSse {
+		s.handleSSE(w, r)
+		return
+	}

Please implement similar error checks where needed to ensure robust URL handling.

📝 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
if path == s.CompleteSsePath() {
pathSse, err := s.CompleteSsePath()
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if path == pathSse {
s.handleSSE(w, r)
return
}

Copy link
Contributor

@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

♻️ Duplicate comments (4)
server/sse.go (4)

358-364: 🛠️ Refactor suggestion

Improve error handling in GetUrlPath method

The GetUrlPath method silently fails by returning an empty string when URL parsing fails. This can lead to subtle bugs if callers don't know to check for empty strings.

-func (s *SSEServer) GetUrlPath(input string) string {
-	parse, err := url.Parse(input)
-	if err != nil {
-		return ""
-	}
-	return parse.Path
-}
+func (s *SSEServer) GetUrlPath(input string) (string, error) {
+	parse, err := url.Parse(input)
+	if err != nil {
+		return "", fmt.Errorf("failed to parse URL %s: %w", input, err)
+	}
+	return parse.Path, nil
+}

366-371: ⚠️ Potential issue

Fix error handling in CompleteSsePath

The CompleteSsePath function doesn't handle potential URL parsing errors from GetUrlPath, which could lead to 404 errors when path matching fails.

func (s *SSEServer) CompleteSseEndpoint() string {
	return s.baseURL + s.basePath + s.sseEndpoint
}
-func (s *SSEServer) CompleteSsePath() string {
-	return s.GetUrlPath(s.CompleteSseEndpoint())
+func (s *SSEServer) CompleteSsePath() string {
+	path := s.GetUrlPath(s.CompleteSseEndpoint())
+	if path == "" {
+		return s.basePath + s.sseEndpoint
+	}
+	return path
+}

373-378: ⚠️ Potential issue

Fix error handling in CompleteMessagePath

Similarly to CompleteSsePath, the CompleteMessagePath function doesn't handle potential URL parsing errors from GetUrlPath.

func (s *SSEServer) CompleteMessageEndpoint() string {
	return s.baseURL + s.basePath + s.messageEndpoint
}
-func (s *SSEServer) CompleteMessagePath() string {
-	return s.GetUrlPath(s.CompleteMessageEndpoint())
+func (s *SSEServer) CompleteMessagePath() string {
+	path := s.GetUrlPath(s.CompleteMessageEndpoint())
+	if path == "" {
+		return s.basePath + s.messageEndpoint
+	}
+	return path
+}

389-390: ⚠️ Potential issue

Fix path comparison for message endpoint

Similar to the SSE endpoint, the message path comparison doesn't handle URL parsing errors.

-	if path == s.CompleteMessagePath() {
+	messagePath := s.CompleteMessagePath()
+	if messagePath != "" && path == messagePath {
		s.handleMessage(w, r)
		return
	}
🧹 Nitpick comments (1)
server/sse.go (1)

61-76: Robust validation for baseURL parameter

The improved validation ensures the baseURL is a properly formatted URL with the correct scheme, host, and no query parameters. This prevents potential issues with URL construction later in the code.

However, consider returning errors instead of using panic, as this would give callers a chance to handle invalid values more gracefully, especially in production code.

-func WithBaseURL(baseURL string) SSEOption {
-	return func(s *SSEServer) {
-		if baseURL != "" {
-			u, err := url.Parse(baseURL)
-			if err != nil {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-			if u.Scheme != "http" && u.Scheme != "https" {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-			// Check if the host is empty or only contains a port
-			if u.Host == "" || strings.HasPrefix(u.Host, ":") {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-			if len(u.Query()) > 0 {
-				panic(fmt.Sprintf("invalid baseURL: %s", baseURL))
-			}
-		}
-		s.baseURL = strings.TrimSuffix(baseURL, "/")
-	}
-}
+// WithBaseURL sets the base URL for the SSE server
+// Returns an error if the URL is invalid
+func WithBaseURL(baseURL string) SSEOption {
+	return func(s *SSEServer) {
+		if baseURL != "" {
+			u, err := url.Parse(baseURL)
+			if err != nil {
+				// Log the error but continue with empty baseURL
+				fmt.Printf("Warning: invalid baseURL ignored: %s - %v\n", baseURL, err)
+				baseURL = ""
+			} else if u.Scheme != "http" && u.Scheme != "https" {
+				fmt.Printf("Warning: invalid scheme in baseURL: %s\n", baseURL)
+				baseURL = ""
+			} else if u.Host == "" || strings.HasPrefix(u.Host, ":") {
+				fmt.Printf("Warning: invalid host in baseURL: %s\n", baseURL)
+				baseURL = ""
+			} else if len(u.Query()) > 0 {
+				fmt.Printf("Warning: baseURL should not contain query parameters: %s\n", baseURL)
+				baseURL = ""
+			}
+		}
+		s.baseURL = strings.TrimSuffix(baseURL, "/")
+	}
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3579581 and fd3f7d8.

📒 Files selected for processing (2)
  • server/sse.go (4 hunks)
  • server/sse_test.go (4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
server/sse_test.go (1)
server/sse.go (7) (7)
  • SSEOption (56-56)
  • WithBasePath (82-90)
  • WithBaseURL (59-79)
  • WithMessageEndpoint (93-97)
  • WithSSEEndpoint (100-104)
  • WithHTTPServer (107-111)
  • NewSSEServer (122-135)
🪛 golangci-lint (1.64.8)
server/sse_test.go

33-33: SA5011: possible nil pointer dereference

(staticcheck)

🔇 Additional comments (5)
server/sse_test.go (4)

8-8: Updated import to support random option testing

The addition of the math/rand import is necessary for the new test case that verifies the consistency of SSEOption when used randomly and repeatedly.


33-36: Updated expected baseURL value to match new implementation

The test now correctly expects "http://localhost:8080" without the path component, which aligns with the changes to URL handling in the implementation.

🧰 Tools
🪛 golangci-lint (1.64.8)

33-33: SA5011: possible nil pointer dereference

(staticcheck)


354-354: Simplified handler registration to directly use sseServer

Using the SSEServer directly with the mux matches the new implementation of URL path handling, where the server now intelligently manages paths internally.


618-661: Good test for ensuring SSEOption consistency

This test properly validates that SSEOption behaves consistently when used repeatedly with random combinations of options, which directly addresses the PR objective of fixing SSEOption behavior.

The test systematically verifies:

  1. Random combinations of options (100 iterations with 100 random options each)
  2. All server properties (basePath, baseURL, sseEndpoint, messageEndpoint, srv)
  3. Consistency of the final configuration regardless of application order
server/sse.go (1)

237-237: Proper use of CompleteMessageEndpoint method

Good refactoring using the new CompleteMessageEndpoint() method instead of manual string formatting, which makes the code more maintainable and consistent.

@ezynda3
Copy link
Contributor

ezynda3 commented Mar 21, 2025

@a67793581 could you please address the coderabbit comments with either a fix or justifcation?

@a67793581
Copy link
Contributor Author

@a67793581 could you please address the coderabbit comments with either a fix or justifcation?

Good, thank you very much for the reminder, I have completed the repair

@ezynda3 ezynda3 merged commit 807a9ba into mark3labs:main Mar 24, 2025
2 checks passed
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.

2 participants