Skip to content

Conversation

@shawn-hurley
Copy link
Contributor

@shawn-hurley shawn-hurley commented Jul 16, 2025

Summary by CodeRabbit

  • New Features

    • Added Unix domain socket support for external providers as an alternative to TCP port-based communication.
    • Added Windows named pipe support for provider connections and initialization.
  • Chores

    • Updated OpenTelemetry, gRPC, and related dependencies to latest versions.
    • Migrated provider communication layer to improved connection handling.

@coderabbitai
Copy link

coderabbitai bot commented Jul 16, 2025

Walkthrough

This pull request introduces Unix domain socket (and Windows named pipe) support for serving providers, migrating from the internal jsonrpc2 package to jsonrpc2_v2 with async RPC patterns, removing handler-based logging infrastructure, updating all external providers to support socket-based serving, and upgrading dependencies across OpenTelemetry, gRPC, and related packages.

Changes

Cohort / File(s) Summary
Socket/Pipe Infrastructure
provider/grpc/socket/consts.go, provider/grpc/socket/uds.go, provider/grpc/socket/pipe_windows.go
New socket abstraction layer with constants, Unix domain socket functions (GetAddress, GetConnectionString, Listen, ConnectGRPC, ConnectRPC), and Windows named pipe equivalents supporting both gRPC and JSON-RPC connections.
Server and Provider Core Updates
provider/server.go, provider/provider.go, provider/grpc/provider.go
Added SocketPath field and socket-based listening support to Server; updated NewServer signature to accept socketPath parameter; added UseSocket and Initialized fields to Config; updated start() function to accept logger and support socket-based binary startup with socket.ConnectGRPC; modified LogProviderOut to standalone function with explicit logger.
External Provider Entry Points
external-providers/dotnet-external-provider/main.go, external-providers/generic-external-provider/main.go, external-providers/java-external-provider/main.go, external-providers/yq-external-provider/main.go
Added socket command-line flag to all providers; updated validation to require either port or socket; modified NewServer calls to include socket parameter.
Generic External Provider Implementation
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go, external-providers/generic-external-provider/Dockerfile, external-providers/generic-external-provider/entrypoint.sh
Changed NewGenericProvider return type to provider.BaseClient; updated Dockerfile base image and added entrypoint.sh script; added copying of entrypoint script and adjusted ENTRYPOINT syntax.
Java External Provider Implementation
external-providers/java-external-provider/pkg/java_external_provider/provider.go, external-providers/java-external-provider/pkg/java_external_provider/service_client.go, external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go
Migrated from jsonrpc2 to jsonrpc2_v2; replaced NewConn with Dial-based connection using base.NewStdDialer; updated RPC invocations to use .Await() pattern; removed depInit() call from test.
YQ External Provider Implementation
external-providers/yq-external-provider/pkg/yq_provider/service_client.go
Removed jsonrpc2 integration and rpc field from yqServiceClient.
LSP Service Client
lsp/base_service_client/base_service_client.go, lsp/base_service_client/std_dialer.go
Updated Conn field from *jsonrpc2.Connection to provider.RPCClient; added handler field; added branching logic to use provided RPC if available; new StdDialer type implementing jsonrpc2.Dialer for stdin/stdout.
gRPC Service Updates
provider/grpc/service_client.go
Added handling for successful provider response with nil Response (returns Matched: false without error).
Protobuf Definitions
provider/internal/grpc/library.proto
Added languageServerPipe (string) and initialized (bool) fields to Config message.
Configuration
provider_container_settings.json, provider_pod_local_settings.json
Added useSocket: true to go and java provider configurations in container settings; added pipeName field to Go provider initConfig in pod settings.
Dependency Updates
go.mod, external-providers/*/go.mod
Updated direct dependencies: go.opentelemetry.io/otel/trace (v1.34.0→v1.35.0), golang.org/x/oauth2 (v0.26.0→v0.28.0), google.golang.org/grpc (v1.72.2→v1.73.0); added github.com/Microsoft/go-winio v0.6.2; updated transitive dependencies (otel family, golang.org/x/net, golang.org/x/sys, golang.org/x/text, google.golang.org/genproto, protobuf).
Dockerfile Updates
Dockerfile, Dockerfile.windows, debug/debug.Dockerfile, demo.Dockerfile
Removed jsonrpc2 directory from COPY statements; added gopls symlink in Dockerfile; added vim and procps to demo.Dockerfile.
Build Configuration
Makefile
Added --entrypoint /usr/local/bin/entrypoint.sh to golang-provider Podman run invocations.
Removed: Old JSON-RPC Package
jsonrpc2/backoff_handler.go, jsonrpc2/handler.go, jsonrpc2/jsonrpc2.go, jsonrpc2/rpcerr.go, jsonrpc2/stream.go, jsonrpc2/wire.go
Entire custom jsonrpc2 package removed, including BackoffHandler, Handler interface with event hooks, Conn type, Stream transport abstraction, wire protocol types (WireRequest, WireResponse, Error, ID), and RPC error utilities (IsRPCClosed, IsOOMError).

Sequence Diagram(s)

sequenceDiagram
    participant Provider as External Provider
    participant Server as provider.Server
    participant Socket as socket Package
    participant GRPC as gRPC Client
    participant Binary as Language Server Binary

    Provider->>Server: NewServer(..., socketPath, log)
    activate Server
    
    rect rgb(200, 220, 255)
    Note over Server: Start() - Socket Mode (UseSocket=true)
    Server->>Socket: Listen(socketPath)
    activate Socket
    Socket-->>Server: net.Listener (Unix socket)
    deactivate Socket
    end
    
    rect rgb(200, 220, 255)
    Note over Server: Start() - Binary Startup with Socket
    Server->>Socket: GetAddress(pipeName)
    activate Socket
    Socket-->>Server: socket address
    deactivate Socket
    
    Server->>Binary: Start binary with --socket and --name
    activate Binary
    Binary->>Socket: GetConnectionString(address)
    activate Socket
    Socket-->>Binary: "unix://socket_path"
    deactivate Socket
    Binary-->>Server: Process started
    deactivate Binary
    end
    
    rect rgb(200, 220, 255)
    Note over Server: Connect to Binary via Socket
    Server->>Socket: ConnectGRPC(connectionString)
    activate Socket
    Socket->>GRPC: grpc.NewClient with custom dialer
    activate GRPC
    GRPC-->>Socket: *grpc.ClientConn
    deactivate GRPC
    Socket-->>Server: gRPC connection
    deactivate Socket
    end
    
    deactivate Server
Loading
sequenceDiagram
    participant Java as Java External Provider
    participant Dialer as base.StdDialer
    participant JSONRPC as jsonrpc2_v2
    participant LSP as LSP Server

    Java->>Dialer: base.NewStdDialer(stdin, stdout)
    activate Dialer
    Dialer-->>Java: *StdDialer (implements jsonrpc2.Dialer)
    deactivate Dialer
    
    Java->>JSONRPC: jsonrpc2.Dial(ctx, dialer, handler)
    activate JSONRPC
    rect rgb(200, 220, 255)
    Note over JSONRPC: Async RPC Pattern
    JSONRPC->>LSP: Send initialize request
    activate LSP
    LSP-->>JSONRPC: initialize response (async)
    deactivate LSP
    JSONRPC-->>Java: *jsonrpc2.Connection
    deactivate JSONRPC
    end
    
    Java->>Java: rpc.Call("workspace/executeCommand").Await(ctx, &refs)
    activate Java
    Note over Java: Async call with Await
    Java-->>Java: Result populated
    deactivate Java
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

This change spans diverse modifications across multiple areas: new socket abstraction layer with platform-specific implementations (Windows pipes, Unix sockets), migration from deprecated internal jsonrpc2 to jsonrpc2_v2 with async patterns, removal of handler-based logging infrastructure, updates to RPC client interfaces and patterns across multiple providers, dependency version bumps, and build configuration changes. While many changes follow repetitive patterns (e.g., adding socket parameter to multiple main.go files), the heterogeneity of concerns (networking abstractions, RPC migration, build updates, removals) and density of API signature changes require careful review of interactions and compatibility.

🐰 With sockets singing and pipes now in place,
The rabbit hops faster through async-bound space,
No more backoff delays in the old handler's way,
Just modernized flows and dependencies play,
Welcome, jsonrpc2_v2—let's seize the day! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Adding beginning of socket communication" is partially related to the changeset. The PR does indeed add socket-based communication support, including Unix domain sockets and Windows named pipes, which is a substantial and real feature within the changes. However, the title does not fully capture the scope of this pull request, which also includes a major migration from the old jsonrpc2 package to jsonrpc2_v2, removal of legacy jsonrpc2 components, updates to multiple external providers, dependency version bumps, and significant refactoring of the RPC client implementation. Additionally, there is a typo in the title: "begining" should be "beginning".
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch from 49dc9a8 to ded14e8 Compare July 16, 2025 20:00
@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch 2 times, most recently from 5fa67ec to be9138d Compare July 24, 2025 01:59
@shawn-hurley shawn-hurley marked this pull request as ready for review July 27, 2025 03:59
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: 6

🧹 Nitpick comments (2)
provider/grpc/socket/pipe_windows.go (1)

49-49: Remove debug print statement.

The debug print statement should be removed from production code as it may leak sensitive information and affects performance.

-	fmt.Printf("pipe: %#v", pipe)

If debugging is needed, use the structured logger passed through context or make it conditional on a debug flag.

provider/grpc/provider.go (1)

290-292: Consider returning error instead of fatal exit.

Using log.Fatalf for provider startup failures may be too aggressive, as it terminates the entire process. Consider returning the error to allow the caller to handle it gracefully.

 		err = cmd.Start()
 		if err != nil {
-			log.Fatalf("unable to start provider: %v", err)
+			return nil, nil, fmt.Errorf("unable to start provider: %w", err)
 		}

This allows for better error handling and recovery strategies in the calling code.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee4445 and 7cc9338.

⛔ Files ignored due to path filters (5)
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/golang-dependency-provider/go.sum is excluded by !**/*.sum
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/yq-external-provider/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (25)
  • Dockerfile (1 hunks)
  • Makefile (1 hunks)
  • external-providers/dotnet-external-provider/Dockerfile (1 hunks)
  • external-providers/dotnet-external-provider/go.mod (1 hunks)
  • external-providers/dotnet-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/Dockerfile (1 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/generic-external-provider/main.go (3 hunks)
  • external-providers/golang-dependency-provider/Dockerfile (1 hunks)
  • external-providers/golang-dependency-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/Dockerfile (1 hunks)
  • external-providers/java-external-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/main.go (3 hunks)
  • external-providers/yq-external-provider/Dockerfile (1 hunks)
  • external-providers/yq-external-provider/go.mod (3 hunks)
  • external-providers/yq-external-provider/main.go (3 hunks)
  • go.mod (3 hunks)
  • provider/grpc/provider.go (7 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/grpc/socket/client_connect.go (1 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/provider.go (1 hunks)
  • provider/server.go (8 hunks)
  • provider_container_settings.json (2 hunks)
🧰 Additional context used
🧠 Learnings (12)
external-providers/generic-external-provider/Dockerfile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/yq-external-provider/Dockerfile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/golang-dependency-provider/Dockerfile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/dotnet-external-provider/go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

Dockerfile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

Makefile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/dotnet-external-provider/Dockerfile (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/java-external-provider/go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

external-providers/golang-dependency-provider/go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

external-providers/yq-external-provider/go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

external-providers/generic-external-provider/go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

go.mod (2)

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The go directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate toolchain directive (introduced in Go 1.21), e.g., toolchain go1.23.11.

Learnt from: shawn-hurley
PR: #863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: Since Go 1.21, the go directive in go.mod files supports patch versions (e.g., "go 1.23.11"), not just major.minor format. The syntax "go 1.23.11" is valid and will not cause build failures in modern Go tooling.

🧬 Code Graph Analysis (7)
provider/grpc/socket/client_connect.go (1)
provider/grpc/socket/pipe_windows.go (2)
  • ConnectGRPC (28-37)
  • MAX_MESSAGE_SIZE (19-19)
external-providers/dotnet-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (64-99)
external-providers/yq-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (64-99)
external-providers/java-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (64-99)
provider/grpc/socket/pipe_windows.go (2)
provider/grpc/socket/uds.go (2)
  • GetSocketAddress (11-20)
  • Listen (22-25)
provider/grpc/socket/client_connect.go (2)
  • ConnectGRPC (14-19)
  • MAX_MESSAGE_SIZE (11-11)
provider/grpc/socket/uds.go (2)
provider/grpc/socket/pipe_windows.go (2)
  • GetSocketAddress (22-27)
  • Listen (39-41)
jsonrpc2_v2/serve.go (1)
  • Listener (18-31)
provider/grpc/provider.go (3)
provider/grpc/socket/uds.go (1)
  • GetSocketAddress (11-20)
provider/grpc/socket/pipe_windows.go (3)
  • GetSocketAddress (22-27)
  • ConnectGRPC (28-37)
  • MAX_MESSAGE_SIZE (19-19)
provider/grpc/socket/client_connect.go (2)
  • ConnectGRPC (14-19)
  • MAX_MESSAGE_SIZE (11-11)
🔇 Additional comments (48)
external-providers/yq-external-provider/Dockerfile (1)

1-1: Resolved: golang:1.23.11 base image exists on Docker Hub

Verified via Docker Hub API that the golang:1.23.11 tag is published. No changes required.

external-providers/dotnet-external-provider/Dockerfile (1)

2-2: Same availability concern for golang:1.23.11 image.

Ensure the tag is published for all target architectures, otherwise the multi-stage build will break.

external-providers/golang-dependency-provider/Dockerfile (1)

1-1: Confirm Go 1.23.11 image exists or fall back to the latest 1.22/1.23 tag.

Build reproducibility depends on the tag being present and immutable.

external-providers/generic-external-provider/Dockerfile (1)

1-1: Potential build breakage if golang:1.23.11 is unpublished.

All provider images moved to this tag; failure in one stage will cascade.
Consider keeping 1.23 or wait until 1.23.11 is officially released.

Dockerfile (1)

4-4: LGTM – Go 1.23.11 Docker image verified

The golang:1.23.11 tag exists on Docker Hub and aligns with our broader modernization effort. No further changes required.

external-providers/java-external-provider/Dockerfile (1)

5-5: LGTM! Consistent Go version upgrade.

The Go base image version update is consistent with the main Dockerfile and other provider Dockerfiles, ensuring uniform build environments across all components.

provider/provider.go (1)

90-90: LGTM! Clean addition enabling socket communication.

The new UseSocket boolean field is well-integrated into the existing Config struct with appropriate YAML and JSON tags. This provides a clean configuration mechanism to choose between TCP ports and Unix domain sockets/named pipes for provider communication.

Makefile (1)

21-21: Approve consistent Go version updates; patch‐version flag is valid

I ran go mod tidy -go=1.23.11 under Go 1.24.1 and confirmed:

  • The go directive is updated to go 1.23.11
  • A toolchain go1.24.1 line is added (expected for the running Go version)

This verifies that using -go=1.23.11 is supported and produces the intended go.mod. No further changes needed.

provider_container_settings.json (1)

5-5: LGTM – selective socket enablement for Go and Java providers

I’ve verified that both the Go provider (generic-external-provider) and the Java provider include the necessary socket flags, and your JSON changes correctly enable useSocket: true for those two.

Notably, socket support is also implemented in the .NET and yq providers (their main.go files contain the same socket flags), but they aren’t enabled in the config. Is this selective rollout intentional, or should we also add "useSocket": true for:

  • dotnet
  • yq

Please confirm the rollout plan or update provider_container_settings.json accordingly.

external-providers/generic-external-provider/main.go (3)

17-17: LGTM: Socket flag addition is correct.

The socket flag follows the established pattern and has appropriate defaults.


56-59: LGTM: Validation logic correctly ensures serving location is specified.

The validation properly requires either a socket or port (or both) to be provided. The error handling is consistent with the existing codebase style.


77-77: LGTM: Server initialization correctly includes socket parameter.

The socket parameter is properly passed to provider.NewServer with correct parameter ordering.

provider/grpc/service_client.go (1)

36-42: LGTM: Excellent defensive programming for nil response handling.

This change prevents potential nil pointer dereferences when the gRPC response is successful but the Response field is nil. The logic correctly returns a "no match" result and includes a clear explanatory comment.

provider/grpc/socket/client_connect.go (3)

1-2: LGTM: Build constraint correctly targets non-Windows platforms.

The build constraint appropriately excludes Windows platforms, complementing the Windows-specific implementation in pipe_windows.go.


3-8: LGTM: Package declaration and imports are appropriate.

Clean imports with only the necessary gRPC dependencies for socket-based connections.


10-19: LGTM: Socket client connection implementation is clean and consistent.

The implementation correctly:

  • Uses the same 8MB message size limit as the Windows implementation
  • Appropriately uses insecure credentials for local socket communication
  • Provides a simple, focused connection interface

The Unix implementation is simpler than the Windows version (which uses passthrough resolver and custom dialer), which is appropriate given the different underlying socket mechanisms.

external-providers/dotnet-external-provider/main.go (3)

21-21: LGTM: Socket flag addition follows established pattern.

Consistent with other external providers in the codebase.


38-41: LGTM: Validation logic is consistent across external providers.

The validation ensures proper server configuration and maintains consistency with other external providers in the codebase.


59-59: LGTM: Server initialization correctly includes socket parameter.

The socket parameter integration is consistent with other external providers and matches the expected function signature.

external-providers/yq-external-provider/main.go (3)

17-17: LGTM: Socket flag addition is consistent with other providers.

The socket flag follows the established pattern with appropriate defaults.


36-39: LGTM: Validation logic maintains consistency across external providers.

The validation ensures proper server configuration with consistent error handling patterns.


56-56: LGTM: Server initialization correctly integrates socket parameter.

The socket parameter is properly passed to provider.NewServer with consistent implementation across external providers.

external-providers/java-external-provider/go.mod (3)

3-5: LGTM! Go version and toolchain directives are correctly formatted.

The use of patch version go 1.23.11 is valid since Go 1.21+, and the separate toolchain go1.24.3 directive correctly specifies the build toolchain version.


8-49: Systematic dependency upgrades look good.

The dependency updates are consistent and align with the broader modernization effort across all external providers. Key upgrades include OpenTelemetry packages to v1.34.0 and gRPC to v1.72.2.


51-51: Local replace directive is appropriate for development.

The replace directive pointing to ../../ facilitates local development against the updated analyzer-lsp module during this PR development cycle.

external-providers/generic-external-provider/go.mod (3)

3-3: Go version update is correctly formatted.

Using go 1.23.11 is valid syntax since Go 1.21+ supports patch versions in the go directive.


25-36: Dependency upgrades are consistent with the modernization effort.

The OpenTelemetry and gRPC version bumps align with other external providers, ensuring consistency across the project.


39-39: Local replace directive supports development workflow.

The replace directive facilitates local development against the updated analyzer-lsp module.

external-providers/golang-dependency-provider/go.mod (3)

3-3: Go version update is consistent with other providers.

The go 1.23.11 format is valid and maintains consistency across all external providers.


23-33: Dependency version bumps align with project-wide updates.

The OpenTelemetry and gRPC upgrades match other providers, ensuring version consistency across the project.


37-37: Replace directive supports local development.

The local path replacement is appropriate for this PR's development workflow.

external-providers/java-external-provider/main.go (3)

17-17: Socket flag implementation is correct.

The socket flag declaration follows the established pattern and provides appropriate documentation.


41-43: Validation logic correctly implements either/or requirement.

The condition properly ensures that either a socket path or port must be provided, making them mutually exclusive alternatives. The error message accurately reflects this requirement.


61-61: Server initialization correctly passes socket parameter.

The call to provider.NewServer properly includes the socket parameter, matching the function signature shown in the relevant code snippets.

external-providers/yq-external-provider/go.mod (3)

3-3: Go version update completes the systematic upgrade.

The go 1.23.11 version is consistent with all other external providers, completing the project-wide Go version standardization.


16-24: Dependency upgrades maintain version consistency.

The OpenTelemetry, gRPC, and other dependency updates align with the versions used across all external providers, ensuring compatibility and consistency.

Also applies to: 32-41


43-43: Replace directive completes the local development setup.

The local path replacement is consistent with all other external providers and supports the development workflow for this PR.

go.mod (3)

21-25: Compatibility Verified: gRPC v1.72.2 & protobuf v1.36.5

No breaking changes detected. Starting in gRPC v1.34.0, the module switched from github.com/golang/protobuf to google.golang.org/protobuf, so using gRPC v1.72.2 with protobuf v1.36.5 is fully supported. No further action required.


3-3: No changes needed for Go version directive

The go 1.23.11 directive is fully supported by Go tooling (patch‐level versions have been accepted since Go 1.21). The failure in go mod verify is due to the sandbox’s empty GOPROXY setting and does not indicate an issue with the go.mod format. You can disregard that error—no update to the go directive is required.


6-6: No known security issues with github.com/Microsoft/go-winio v0.6.2
A review of the Go Vulnerability Database and GitHub advisories shows no reported vulnerabilities for version 0.6.2. This dependency addition is safe. Continue to monitor the Go Vulnerability Database (https://pkg.go.dev/vuln/list?utm_source=openai) and the official repo for any future security updates.

provider/server.go (4)

64-64: LGTM - Server constructor properly extended for socket support.

The NewServer function correctly accepts the new socketPath parameter and integrates it into the server struct. The parameter ordering and validation logic are appropriate.


104-110: LGTM - Robust server startup validation.

The conditional logic properly ensures that either a port or socket path is provided, preventing invalid server configurations. The error message is clear and actionable.


131-131: LGTM - Consistent use of socket package constants.

The migration from hardcoded provider.MAX_MESSAGE_SIZE to socket.MAX_MESSAGE_SIZE ensures consistency across the socket communication implementation.


492-492: LGTM - JWT callback signature updated correctly.

The change from (interface{}, error) to (any, error) aligns with modern Go conventions and the jwt library's expected signature. This is a good modernization.

provider/grpc/provider.go (4)

49-51: LGTM - Improved provider output logging.

The refactoring of LogProviderOut to a standalone function and running it as a goroutine immediately after process start is a good improvement. This ensures provider output is captured even if there are issues during connection establishment.


267-281: LGTM - Clean socket vs TCP connection logic.

The conditional logic properly handles both socket and TCP connections based on the UseSocket configuration flag. The connection string generation is appropriate for each transport type.


294-294: LGTM - Unified gRPC connection handling.

The use of socket.ConnectGRPC provides a consistent interface for both socket and TCP connections, which is a good architectural decision.


304-305: LGTM - Consistent message size configuration.

The migration to socket.MAX_MESSAGE_SIZE ensures consistent maximum message size across all connection types (socket and TCP).

@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch from 7cc9338 to 050d4c0 Compare July 28, 2025 15:14
@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch from ceedb6c to c0ca331 Compare October 17, 2025 13:42
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: 0

🧹 Nitpick comments (2)
provider/grpc/socket/pipe_windows.go (1)

43-51: Remove debug print statement before production release.

Line 49 contains a debug fmt.Printf that should be removed or replaced with proper logging. Additionally, line 44's strings.CutPrefix result is ignored; consider logging if the prefix is missing.

Apply this diff:

 func DialWindowsPipePassthrough(ctx context.Context, connectionString string) (net.Conn, error) {
-	pipeName, _ := strings.CutPrefix(connectionString, "unix://")
+	pipeName, found := strings.CutPrefix(connectionString, "unix://")
+	if !found {
+		return nil, fmt.Errorf("invalid pipe connection string: expected unix:// prefix")
+	}
 	pipe, err := winio.DialPipeContext(ctx, pipeName)
 	if err != nil {
 		return nil, err
 	}
-	fmt.Printf("pipe: %#v", pipe)
 	return pipe, nil
 }
provider/grpc/provider.go (1)

285-289: Remove debug print statement before production release.

Line 285 contains a debug fmt.Printf that outputs the full command. This should be removed or replaced with proper logging at an appropriate level.

Apply this diff:

-		fmt.Printf("\ncommand: %v\n", cmd)
+		log.V(5).Info("starting provider command", "command", cmd.String())
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ceedb6c and c0ca331.

⛔ Files ignored due to path filters (2)
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • external-providers/dotnet-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/main.go (3 hunks)
  • external-providers/java-external-provider/go.mod (2 hunks)
  • external-providers/java-external-provider/main.go (3 hunks)
  • external-providers/yq-external-provider/main.go (3 hunks)
  • go.mod (4 hunks)
  • provider/grpc/provider.go (4 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/grpc/socket/consts.go (1 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/provider.go (1 hunks)
  • provider/server.go (8 hunks)
  • provider_container_settings.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • provider/provider.go
  • external-providers/dotnet-external-provider/main.go
  • provider/grpc/socket/consts.go
  • external-providers/generic-external-provider/main.go
  • provider_container_settings.json
  • provider/grpc/socket/uds.go
  • external-providers/java-external-provider/main.go
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-28T15:28:31.377Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-28T15:27:30.233Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:28-37
Timestamp: 2025-07-28T15:27:30.233Z
Learning: In Windows named pipe gRPC connections, the "unix://" URI scheme prefix must be used instead of a more semantically correct scheme like "pipe://" because it's required for the gRPC package to provide the desired behavior. This is an intentional design choice despite being semantically incorrect.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-25T02:05:02.884Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The `go` directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate `toolchain` directive (introduced in Go 1.21), e.g., `toolchain go1.23.11`.

Applied to files:

  • external-providers/java-external-provider/go.mod
🧬 Code graph analysis (5)
provider/grpc/provider.go (4)
provider/provider.go (1)
  • Config (86-96)
provider/grpc/socket/pipe_windows.go (3)
  • GetAddress (17-22)
  • GetConnectionString (24-26)
  • ConnectGRPC (28-37)
provider/grpc/socket/uds.go (3)
  • GetAddress (14-23)
  • GetConnectionString (25-27)
  • ConnectGRPC (33-40)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
provider/grpc/service_client.go (3)
jsonrpc2_v2/messages.go (1)
  • Response (42-49)
provider/provider.go (1)
  • ProviderEvaluateResponse (295-299)
provider/internal/grpc/library.pb.go (3)
  • ProviderEvaluateResponse (470-477)
  • ProviderEvaluateResponse (490-490)
  • ProviderEvaluateResponse (505-507)
provider/server.go (4)
provider/provider.go (1)
  • BaseClient (442-446)
provider/grpc/socket/pipe_windows.go (1)
  • Listen (39-41)
provider/grpc/socket/uds.go (1)
  • Listen (29-31)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
provider/grpc/socket/pipe_windows.go (2)
provider/grpc/socket/uds.go (4)
  • GetAddress (14-23)
  • GetConnectionString (25-27)
  • ConnectGRPC (33-40)
  • Listen (29-31)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
external-providers/yq-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (64-99)
⏰ 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: test
  • GitHub Check: test (windows-latest)
🔇 Additional comments (15)
go.mod (1)

6-6: LGTM! Dependency updates align with socket communication feature.

The addition of go-winio v0.6.2 enables Windows named pipe support, and the updates to gRPC (v1.73.0), OpenTelemetry (v1.35.0), and related dependencies are appropriate for the new socket-based transport functionality.

Based on learnings: gRPC v1.73.0 enables least-request LB by default and adds CallAuthority option; go-winio v0.6.2 provides Windows pipe utilities with Go 1.21+ support.

Also applies to: 21-25, 38-39, 50-55

external-providers/java-external-provider/go.mod (1)

5-5: LGTM! Toolchain and dependency updates are appropriate.

The toolchain go1.24.3 directive correctly specifies the required Go toolchain version while keeping the module version at 1.23.9. All dependency updates are minor/patch version bumps that align with the broader socket communication feature.

Based on learnings: The toolchain directive is the correct approach for specifying patch-level Go version requirements (introduced in Go 1.21).

Also applies to: 8-8, 10-10, 18-18, 24-24, 34-34, 38-41

provider/grpc/service_client.go (1)

36-41: LGTM! Defensive nil check prevents potential panic.

This correctly handles the case where a provider returns success without a response payload, treating it as no match. This prevents a nil pointer dereference at line 43 where r.Response.Matched would be accessed.

external-providers/yq-external-provider/main.go (1)

17-17: LGTM! Socket support correctly integrated.

The socket flag, validation logic, and updated NewServer call correctly implement socket-based startup alongside the existing port-based approach. The validation ensures exactly one serving location is provided.

Also applies to: 36-39, 56-56

provider/server.go (4)

64-64: LGTM! Signature update correctly adds socket support.

The NewServer signature now accepts a socketPath parameter, enabling socket-based serving alongside TCP. This is consistently propagated to the server struct.

Also applies to: 91-91


102-110: LGTM! Clean listener selection logic.

The mutual exclusion between Port and SocketPath is correctly validated, with appropriate error handling. The socket.Listen abstraction cleanly handles platform-specific differences (Unix domain sockets vs. Windows named pipes).


131-131: LGTM! Correct use of shared MAX_MESSAGE_SIZE constant.

Using socket.MAX_MESSAGE_SIZE from the shared constants file eliminates duplication and ensures consistency across the codebase.


492-492: LGTM! Cosmetic type alias update.

Changing interface{} to any is a cosmetic improvement using Go 1.18+'s type alias. Both are equivalent.

provider/grpc/socket/pipe_windows.go (4)

17-21: LGTM! Pipe name generation is appropriate.

The use of math/rand for generating random pipe suffixes is sufficient for collision avoidance in local IPC scenarios.

Based on learnings: Cryptographic security is not required for Windows named pipe address generation in this project.


24-26: LGTM! Required gRPC URI scheme format.

The passthrough:unix:// prefix is required for gRPC to provide correct behavior with the custom dialer, despite being semantically incorrect for Windows pipes.

Based on learnings: The unix:// scheme must be used for gRPC compatibility with the custom dialer, even on Windows.


28-37: LGTM! Windows pipe gRPC client properly configured.

The client correctly uses a custom dialer with passthrough resolver, appropriate message size limits, insecure credentials (for local IPC), and localhost authority.


39-41: LGTM! Named pipe listener creation.

Using winio.ListenPipe with default config is appropriate for creating a Windows named pipe listener.

provider/grpc/provider.go (3)

44-44: LGTM! Logger threading properly implemented.

The start function signature now accepts a logger, enabling proper logging throughout the startup process and in LogProviderOut. This is a clean improvement over the previous implementation.

Also applies to: 249-249, 344-344


263-277: LGTM! Clean socket vs. port selection logic.

The conditional logic clearly separates socket-based and port-based startup paths, with appropriate error handling and command construction for each case.


295-300: LGTM! Connection logic properly updated.

All connection paths now correctly use:

  • socket.ConnectGRPC for socket-based connections with proper abstractions
  • grpc.NewClient (newer API) for direct address connections
  • socket.MAX_MESSAGE_SIZE for consistent message size limits
  • Proper error handling and logging throughout

The refactoring consolidates connection logic while maintaining support for TLS and JWT authentication paths.

Also applies to: 304-312, 314-340

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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
demo.Dockerfile (1)

20-20: Non-POSIX redirection may fail under /bin/sh

&> is bash-specific. Use portable redirection.

-ENTRYPOINT ["sh", "-c", "all-in-one-linux &> /dev/null & sleep 5 && konveyor-analyzer --enable-jaeger && curl -o traces.json http://localhost:16686/api/traces?service=analyzer-lsp"]
+ENTRYPOINT ["sh", "-c", "all-in-one-linux >/dev/null 2>&1 & sleep 5 && konveyor-analyzer --enable-jaeger && curl -o traces.json http://localhost:16686/api/traces?service=analyzer-lsp"]
external-providers/java-external-provider/pkg/java_external_provider/service_client.go (3)

205-211: Guard type assertion when filtering by filepaths

ref.Location.Value may not be protocol.Location; current code can panic.

-                if strings.HasSuffix(ref.Location.Value.(protocol.Location).URI, fp) {
+                if loc, ok := ref.Location.Value.(protocol.Location); ok && strings.HasSuffix(loc.URI, fp) {
                     filteredRefs = append(filteredRefs, ref)
                 }

192-200: jsonrpc2.IsRPCClosed may not exist in v2; make detection robust

The v2 package likely doesn’t expose IsRPCClosed. Use a local helper to detect closed/EOF conditions and avoid compile errors.

-        if jsonrpc2.IsRPCClosed(err) {
+        if isRPCClosed(err) {
             log.Error(err, "connection to the language server is closed, language server is not running")
             return refs, fmt.Errorf("connection to the language server is closed, language server is not running")
         } else {

And similarly in GetAllReferences and initialization.

Add this helper in the same file:

// near the top of the file
func isRPCClosed(err error) bool {
    if err == nil {
        return false
    }
    return errors.Is(err, io.EOF) ||
        errors.Is(err, context.Canceled) ||
        errors.Is(err, syscall.EPIPE) ||
        strings.Contains(strings.ToLower(err.Error()), "use of closed") ||
        strings.Contains(strings.ToLower(err.Error()), "broken pipe") ||
        strings.Contains(strings.ToLower(err.Error()), "file already closed")
}

Also applies to: 255-261, 413-417


311-321: Ensure graceful shutdown closes RPC and cancels context

After shutdown/exit, close the client and cancel to stop goroutines and close pipes deterministically.

 err = p.rpc.Notify(shutdownCtx, "exit", nil)
 if err != nil {
   p.log.Error(err, "failed to send exit notification to language server")
   return err
 }
+// Close RPC connection and cancel the service context
+if cerr := p.rpc.Close(); cerr != nil {
+  p.log.V(7).Error(cerr, "rpc close error (ignored)")
+}
+if p.cancelFunc != nil {
+  p.cancelFunc()
+}
 return nil
external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

936-944: Indexing bug parsing Gradle unresolved sources

Accessing match[4] is out of bounds; use gav[4] from artifactRegex.

-        if gav[4] != "" { // internal library, unknown group/version
-            artifact := javaArtifact{
-                ArtifactId: match[4],
-            }
+        if gav[4] != "" { // internal library, unknown group/version
+            artifact := javaArtifact{
+                ArtifactId: gav[4],
+            }

Add/adjust a unit test covering this branch to prevent regressions.

🧹 Nitpick comments (11)
lsp/base_service_client/base_handlers.go (5)

83-86: Prefer integer retries and bit-shift over float Pow.

Using float64 for retries + math.Pow is unnecessary and risks rounding. An int with 1<<retries is simpler and faster.

Apply this refactor:

-type backoffTimer struct {
-	mu                sync.Mutex
-	retries           float64
-	lastAttemptedTime *time.Time
-	lastDurationTime  time.Duration
-}
+type backoffTimer struct {
+	mu                sync.Mutex
+	retries           int
+	lastAttemptedTime *time.Time
+	lastDurationTime  time.Duration
+}
@@
-	// calculated back off
-	b.lastDurationTime = time.Second * time.Duration((math.Pow(2, b.retries)))
+	// calculated backoff (2^retries seconds)
+	b.lastDurationTime = time.Second * time.Duration(1<<uint(b.retries))

Also applies to: 106-116


77-80: Keying by raw Params string is brittle and memory-heavy.

string(req.Params) duplicates payload bytes; semantically equivalent JSON can hash differently (whitespace/order). Consider a stable, compact key.

Option A (hash only):

sum := sha256.Sum256(req.Params)
key := requestKey{method: req.Method, params: hex.EncodeToString(sum[:])}

Option B (canonicalize then hash):

  • Decode to interface{}, re-marshal with stable ordering, then hash.
  • Or rely on a canonical JSON lib if available.

Also applies to: 129-133


64-66: Unbounded map growth; add TTL/eviction.

failedRequests grows per unique method+params with no cleanup; long-running processes can leak memory.

  • Track last access and evict entries idle > N minutes via a background sweeper or lazy cleanup on insert.
  • Alternatively, cap size with LRU semantics.

118-126: Stale/misleading comment block.

This “Log is invoked...” comment doesn’t describe BackoffHandler.Handle; it seems copied from a different API.

Apply this diff:

-// Log is invoked for all messages flowing through a Conn.
-// direction indicates if the message being received or sent
-// id is the message id, if not set it was a notification
-// elapsed is the time between a call being seen and the response, and is
-// negative for anything that is not a response.
-// method is the method name specified in the message
-// payload is the parameters for a call or notification, and the result for a
-// response
-// Request is called near the start of processing any request.
+// Handle applies per-request exponential backoff before passing the request
+// down the handler chain. It always returns jsonrpc2.ErrNotHandled.

99-104: Small nits: simplify duration literal; avoid redundant mutex init.

  • time.Minute * 1 -> time.Minute
  • failedRequestsMu is zero-value initialized; explicit init in ctor is unnecessary.
-	if time.Now().After(b.lastAttemptedTime.Add(b.lastDurationTime).Add(time.Minute * 1)) {
+	if time.Now().After(b.lastAttemptedTime.Add(b.lastDurationTime).Add(time.Minute)) {
-		failedRequestsMu: sync.Mutex{},

No behavior change.

Also applies to: 110-112, 72-72

demo.Dockerfile (2)

2-2: Local base image will break remote builds

FROM localhost/... won’t be pullable in CI or by contributors. Use a public registry image or make it a build-arg defaulting to quay.io.

- FROM localhost/testing/analyzer-lsp:testing-socket
+ ARG BASE_IMAGE=quay.io/konveyor/analyzer-lsp:latest
+ FROM ${BASE_IMAGE}

15-15: Slim image and cache hygiene

Clean dnf metadata and layer installs together to reduce size.

-RUN microdnf install go-toolset vim procps -y
+RUN microdnf -y install go-toolset vim procps \
+ && microdnf -y clean all
provider/provider.go (2)

154-156: Consider omitempty for PipeName

Avoid emitting empty fields in JSON/YAML.

- PipeName string `yaml:"pipeName" json:"pipeName"`
+ PipeName string `yaml:"pipeName,omitempty" json:"pipeName,omitempty"`

91-91: Document UseSocket

Add a short comment clarifying precedence: Address vs UseSocket/PipeName.

- UseSocket    bool         `yaml:"useSocket,omitempty" json:"useSocket,omitempty"`
+ // When true, start/connect via Unix domain socket (or Windows named pipe via PipeName) instead of TCP Address.
+ UseSocket    bool         `yaml:"useSocket,omitempty" json:"useSocket,omitempty"`
provider_container_settings.json (1)

12-12: Absolute gopls path

Hardcoding /root/go/bin/gopls ties to root user. If possible, derive from GOBIN/GOPATH or make it configurable.

external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

410-421: Initialization retry without backoff

Tight loop retries can spin CPU. Add a short sleep or exponential backoff.

 for i := 0; i < 10; i++ {
   err := p.rpc.Call(ctx, "initialize", params).Await(ctx, &result)
   if err != nil {
     ...
-    continue
+    time.Sleep(500 * time.Millisecond)
+    continue
   }
   break
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0ca331 and 4a77781.

⛔ Files ignored due to path filters (3)
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/yq-external-provider/go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • Dockerfile (0 hunks)
  • Dockerfile.windows (0 hunks)
  • debug/debug.Dockerfile (0 hunks)
  • demo.Dockerfile (2 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/java-external-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/service_client.go (6 hunks)
  • external-providers/yq-external-provider/go.mod (2 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go (0 hunks)
  • jsonrpc2/backoff_handler.go (0 hunks)
  • jsonrpc2/handler.go (0 hunks)
  • jsonrpc2/jsonrpc2.go (0 hunks)
  • jsonrpc2/rpcerr.go (0 hunks)
  • jsonrpc2/stream.go (0 hunks)
  • jsonrpc2/wire.go (0 hunks)
  • lsp/base_service_client/base_handlers.go (2 hunks)
  • lsp/base_service_client/std_dialer.go (1 hunks)
  • provider/provider.go (3 hunks)
  • provider_container_settings.json (3 hunks)
💤 Files with no reviewable changes (10)
  • debug/debug.Dockerfile
  • jsonrpc2/stream.go
  • Dockerfile.windows
  • Dockerfile
  • jsonrpc2/rpcerr.go
  • jsonrpc2/wire.go
  • jsonrpc2/handler.go
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go
  • jsonrpc2/backoff_handler.go
  • jsonrpc2/jsonrpc2.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-30T12:11:45.673Z
Learnt from: pranavgaikwad
PR: konveyor/analyzer-lsp#859
File: external-providers/java-external-provider/pkg/java_external_provider/dependency.go:694-694
Timestamp: 2025-07-30T12:11:45.673Z
Learning: In the Java external provider dependency walker (external-providers/java-external-provider/pkg/java_external_provider/dependency.go), errors from toDependency function calls should be ignored as they are not considered important by the maintainers.

Applied to files:

  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go
🧬 Code graph analysis (4)
lsp/base_service_client/base_handlers.go (2)
jsonrpc2_v2/jsonrpc2.go (2)
  • Handler (60-75)
  • ErrNotHandled (24-24)
jsonrpc2_v2/messages.go (1)
  • Request (30-38)
lsp/base_service_client/std_dialer.go (1)
jsonrpc2_v2/serve.go (1)
  • Dial (57-64)
provider/provider.go (1)
jsonrpc2_v2/conn.go (1)
  • AsyncCall (375-381)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (5)
lsp/base_service_client/std_dialer.go (1)
  • NewStdDialer (24-31)
jsonrpc2_v2/serve.go (1)
  • Dial (57-64)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (2)
  • NewChainHandler (36-48)
  • LogHandler (22-27)
⏰ 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). (4)
  • GitHub Check: test
  • GitHub Check: test (windows-latest)
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (macos-latest, mac)
🔇 Additional comments (10)
external-providers/java-external-provider/go.mod (4)

3-5: Verify toolchain version is intentional.

The Go module version is pinned to 1.23.9, but the toolchain directive specifies go1.24.3. While this is syntactically valid (it allows compilation with a newer toolchain while maintaining 1.23.9 compatibility), confirm that this version mismatch is intentional.


13-13: Clarify indirect marking on direct grpc require.

The google.golang.org/grpc dependency is listed in the direct require block with an // indirect comment, which is unusual. Confirm whether this is intentional (e.g., for version pinning) or whether it should be moved to an indirect section.


24-24: Windows named pipes dependency aligns with socket communication feature.

The addition of github.com/Microsoft/go-winio v0.6.2 as an indirect dependency is appropriate for the new Windows named pipes support in the socket communication feature introduced by this PR.


1-52: Verify all dependency updates are compatible and necessary.

This commit includes routine dependency updates across OpenTelemetry, grpc, and other packages. Most are patch or minor version bumps. Ensure that:

  • All updates are compatible with the new socket communication feature.
  • There are no known security advisories for the updated versions.
  • The dependency resolver has no conflicts or warnings.

Running go mod tidy and go mod verify in this module would help confirm consistency.

provider_container_settings.json (1)

5-5: No action required; socket implementation is correct

The useSocket: true flag already takes effect as intended. When enabled, the code reads the flag (provider/grpc/provider.go line 263), obtains the socket path via socket.GetAddress(), and launches the provider binary with the --socket argument (line 268). The runtime wiring is properly configured.

external-providers/yq-external-provider/go.mod (1)

16-16: Dependencies are tidy and cross-platform verified

go mod tidy confirmed no changes to go.mod/go.sum—the files are already properly tidied. go-winio v0.6.2 is correctly marked as indirect. All transitive dependencies resolved without issue.

provider/provider.go (1)

159-162: Interface change ripple-check: All implementers and call sites properly updated

Verification confirms all changed files correctly implement the new RPCClient interface:

  • java-external-provider/service_client.go: All four Call() sites properly chain .Await() (lines 191, 254, 312, 411)
  • yq-external-provider: Migrated away from old package entirely
  • base_service_client: Updated imports to jsonrpc2_v2
  • IsRPCClosed() function and usage remain valid

The interface transition from Call(...interface{}, interface{}) error to Call(...any) *AsyncCall has been fully and correctly implemented across all modified files in this PR.

external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go (1)

541-541: Review comment is incorrect—depInit() method does not exist

The javaServiceClient type has no depInit() method. The commented line on 541 (//p.depInit()) is already documented with the explanation "we are not testing dep init here, so ignore error" and poses no risk of silently changing defaults or caches. The code is correct as written.

Likely an incorrect or invalid review comment.

lsp/base_service_client/std_dialer.go (1)

47-54: LGTM!

The Close method correctly closes both underlying streams and properly combines errors using errors.Join.

external-providers/generic-external-provider/go.mod (1)

16-37: Dependency versions are secure but OpenTelemetry could be more current.

Security check confirmed no known vulnerabilities for go-winio v0.6.2 or the OpenTelemetry packages. However, OpenTelemetry latest versions were published Aug 29, 2025, while the go.mod specifies v1.35.0. More recent semantic convention versions (v1.37.0) are available. Consider updating to the latest OpenTelemetry versions for improved stability and features.

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: 6

🧹 Nitpick comments (6)
external-providers/generic-external-provider/entrypoint.sh (1)

3-4: Hardcoded socket path and missing error handling pose risks for multi-instance deployments.

The script starts gopls listening on a hardcoded /tmp/go-pls-pipe-test socket without waiting for it to be ready or checking for errors. If gopls fails to start, the generic-external-provider will still launch, leading to runtime failures. Additionally, /tmp is inappropriate for production use (subject to cleanup, permission issues) and will conflict if multiple containers run on the same host or if the container/pod model changes (e.g., Kubernetes).

Consider:

  1. Making the socket path configurable (e.g., via environment variable)
  2. Adding error handling and health checks before launching the provider
  3. Using a persistent, writable directory suitable for production (e.g., a mounted volume or /var/run)
external-providers/generic-external-provider/Dockerfile (1)

37-37: ENTRYPOINT design is sound but could benefit from documentation.

The default ENTRYPOINT points to generic-external-provider directly, while the Makefile explicitly overrides it with --entrypoint /usr/local/bin/entrypoint.sh for golang-provider. This is a reasonable design (gopls startup is golang-specific), but it's implicit and could be clearer via comments or documentation.

Consider adding a Dockerfile comment explaining when/why the entrypoint override is used.

provider/grpc/socket/pipe_windows.go (2)

26-28: Document intentional use of unix:// on Windows for gRPC passthrough.

Add a comment to prevent future “fixes” back to pipe://.

 func GetConnectionString(address string) string {
-	return fmt.Sprintf("passthrough:unix://%s", address)
+	// Intentionally use unix:// on Windows with the passthrough resolver so the raw
+	// target is handed to our context dialer. This is required for desired gRPC behavior.
+	// See DialWindowsPipePassthrough. (Semantics > syntax.) 
+	return fmt.Sprintf("passthrough:unix://%s", address)
 }

Based on learnings.


41-43: Consider exposing PipeConfig/SDDL or documenting access semantics.

Default PipeConfig may restrict cross-user access; some deployments may need an explicit SDDL. Optionally accept a PipeConfig or env-based SDDL, defaulting to current behavior.

Would you like a small helper (functional option or env var) to pass SecurityDescriptor without widening default permissions?

provider/server.go (2)

103-111: Unix socket startup: remove stale path before Listen.

Listening on an existing UDS file fails; pre-remove it.

Apply:

  if s.Port != 0 {
     listen, err = net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
  } else if s.SocketPath != "" {
+    // Best-effort remove any stale socket file
+    _ = os.Remove(s.SocketPath)
     listen, err = socket.Listen(s.SocketPath)
  } else {
     return fmt.Errorf("unable to start server no serving information present")
  }

145-146: Graceful shutdown on ctx.Done.

Start currently ignores ctx; hook it to stop gRPC and close the listener to match the contract.

Apply:

   s.Log.Info(fmt.Sprintf("server listening at %v", listen.Addr()))
+  go func() {
+    <-ctx.Done()
+    s.Log.Info("server context canceled; shutting down")
+    gs.GracefulStop()
+    _ = listen.Close()
+    if s.SocketPath != "" {
+      _ = os.Remove(s.SocketPath)
+    }
+  }()
   if err := gs.Serve(listen); err != nil {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a77781 and 4be055b.

⛔ Files ignored due to path filters (2)
  • provider/internal/grpc/library.pb.go is excluded by !**/*.pb.go
  • provider/internal/grpc/library_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • Makefile (2 hunks)
  • external-providers/generic-external-provider/Dockerfile (1 hunks)
  • external-providers/generic-external-provider/entrypoint.sh (1 hunks)
  • lsp/base_service_client/base_service_client.go (2 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/internal/grpc/library.proto (2 hunks)
  • provider/server.go (10 hunks)
  • provider_pod_local_settings.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • provider/grpc/socket/uds.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-28T15:28:31.377Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-28T15:27:30.233Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:28-37
Timestamp: 2025-07-28T15:27:30.233Z
Learning: In Windows named pipe gRPC connections, the "unix://" URI scheme prefix must be used instead of a more semantically correct scheme like "pipe://" because it's required for the gRPC package to provide the desired behavior. This is an intentional design choice despite being semantically incorrect.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
🧬 Code graph analysis (3)
lsp/base_service_client/base_service_client.go (6)
provider/provider.go (1)
  • RPCClient (158-162)
jsonrpc2_v2/serve.go (2)
  • Dialer (34-37)
  • Dial (57-64)
lsp/base_service_client/cmd_dialer.go (1)
  • NewCmdDialer (25-55)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (1)
  • NewChainHandler (36-48)
provider/grpc/socket/pipe_windows.go (3)
provider/grpc/socket/uds.go (5)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
  • Listen (31-33)
  • ConnectRPC (44-51)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/serve.go (2)
  • Listener (18-31)
  • Dial (57-64)
provider/server.go (6)
provider/provider.go (1)
  • BaseClient (447-451)
jsonrpc2_v2/serve.go (1)
  • Listener (18-31)
provider/grpc/socket/pipe_windows.go (3)
  • Listen (41-43)
  • GetAddress (19-24)
  • ConnectRPC (55-62)
provider/grpc/socket/uds.go (3)
  • Listen (31-33)
  • GetAddress (16-25)
  • ConnectRPC (44-51)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/conn.go (1)
  • Connection (66-79)
⏰ 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). (4)
  • GitHub Check: test
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (macos-latest, mac)
  • GitHub Check: test (windows-latest)
🔇 Additional comments (6)
Makefile (1)

59-61: Naming inconsistency: redundant and conflicting --name arguments.

Lines 60–61 pass --name arguments twice: once as a podman container name and again in the provider arguments. This is confusing:

  • Line 60: --name nodejs ... --name nodejs (redundant)
  • Line 61: --name python ... --name pylsp (inconsistent — container vs. provider name mismatch)

Clarify the intent: are the second --name arguments meant for the provider, or are they artifacts? If intentional, document or simplify.

provider/grpc/socket/pipe_windows.go (1)

5-12: Avoid deterministic pipe names; switch to pid+time suffix and fix var casing.

math/rand without seeding yields a deterministic sequence across restarts. Use non-crypto uniqueness via pid + time and follow Go camelCase.

Apply:

@@
-import (
+import (
 	"context"
 	"fmt"
 	"io"
-	"math/rand"
+	"os"
+	"time"
 	"net"
 	"strings"
@@
 func GetAddress(name string) (string, error) {
-	randInt := rand.Int()
-	pipe_name := fmt.Sprintf("\\\\.\\pipe\\%s-%v", name, randInt)
-	return pipe_name, nil
+	pipeName := fmt.Sprintf("\\\\.\\pipe\\%s-%d-%d", name, os.Getpid(), time.Now().UnixNano())
+	return pipeName, nil
 }

This stays non-cryptographic while eliminating deterministic collisions. Based on learnings.

Also applies to: 19-24

⛔ Skipped due to learnings
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.
lsp/base_service_client/base_service_client.go (1)

129-129: No breaking API changes found — verify review comment concerns are moot.

The provider.RPCClient interface is properly defined with all required methods (Call, Notify, Close). All usages throughout the codebase—including in base_service_client.go and external providers—are compatible with the interface. The Await semantics are preserved as Call() returns *jsonrpc2.AsyncCall, and error handling for notifications is maintained. No compilation failures or breaking changes exist.

provider/server.go (1)

65-65: NewServer signature updated across all binaries.

All call sites in external-providers (dotnet, yq, java, generic) correctly pass the socketPath argument. No test updates required.

provider_pod_local_settings.json (1)

17-19: Review comment is incorrect — "pipeName" is the correct key.

The review conflates two different config structures. The file provider_pod_local_settings.json is parsed by provider/provider.go's GetConfig() function, which unmarshals into the local provider.Config struct. This struct defines the field with YAML/JSON tags yaml:"pipeName" json:"pipeName" (line 155 in provider.go).

The languageServerPipe field exists in library.proto as part of the gRPC protocol's Config message, which is used for inter-process communication, not for parsing this local configuration file.

The current JSON key "pipeName" is correct and should not be changed.

Likely an incorrect or invalid review comment.

provider/internal/grpc/library.proto (1)

23-23: Codegen is already complete; verify whether languageServerPipe should be propagated in the response Config.

The generated Go code in provider/internal/grpc/library.pb.go is already current with the proto definition—the struct field and getter methods exist. However, the config writer at provider/server.go:282-286 creates builtinRpcConf but only sets Location and DependencyPath. The incoming config.LanguageServerPipe is read (line 253-254) and used to initialize an RPC client, but it is not propagated to the response InitResponse.BuiltinConfig. Confirm whether LanguageServerPipe should be included in the outgoing response config or if it is intentionally consumed only for initialization.

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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
demo.Dockerfile (1)

20-20: ENTRYPOINT lacks service readiness checks and error handling.

The ENTRYPOINT assumes Jaeger (all-in-one-linux) is ready after a fixed 5-second sleep, which is unreliable. If the service takes longer to start or fails, the analyzer may try to connect to a non-functional Jaeger instance. Additionally, output is redirected to /dev/null, hiding logs that would help debug startup issues.

Consider adding proper service readiness checks:

-ENTRYPOINT ["sh", "-c", "all-in-one-linux &> /dev/null & sleep 5 && konveyor-analyzer --enable-jaeger && curl -o traces.json http://localhost:16686/api/traces?service=analyzer-lsp"]
+ENTRYPOINT ["sh", "-c", "all-in-one-linux & sleep 10 && until curl -f http://localhost:16686/status > /dev/null 2>&1; do sleep 1; done && konveyor-analyzer --enable-jaeger && curl -o traces.json http://localhost:16686/api/traces?service=analyzer-lsp"]

Or consider using a health check script or Docker Compose for better orchestration.

Would you like me to create a more robust service startup pattern, perhaps using a separate health-check script or Docker Compose configuration?

♻️ Duplicate comments (9)
external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1)

190-191: Fix context leak on timeout.

The cancel function from context.WithTimeout must be called to release timers.

Apply this diff:

-	timeOutCtx, _ := context.WithTimeout(ctx, timeout)
-	err = p.rpc.Call(timeOutCtx, "workspace/executeCommand", wsp).Await(timeOutCtx, &refs)
+	timeOutCtx, cancel := context.WithTimeout(ctx, timeout)
+	defer cancel()
+	err = p.rpc.Call(timeOutCtx, "workspace/executeCommand", wsp).Await(timeOutCtx, &refs)
lsp/base_service_client/base_handlers.go (2)

82-116: Data race + incorrect reset window in backoffTimer.

This issue has already been flagged in previous reviews: backoffTimer lacks synchronization, allowing concurrent Handle() calls to race on the retries, lastAttemptedTime, and lastDurationTime fields. Additionally, lastAttemptedTime is not updated after each call, causing the reset window calculation to be incorrect.


145-149: Respect context cancellation; avoid blind sleep.

This issue has already been flagged in previous reviews: time.Sleep(d) blocks for up to 5 minutes even if the context is canceled or the connection drops. The sleep should be context-aware using a timer and select statement.

lsp/base_service_client/std_dialer.go (2)

11-16: Fix type name in comments; remove misleading process note.

-// Dialers in jsonrpc2_v2 return a ReadWriteCloser that sends and receives
-// information from the server. CmdDialer functions as a both a ReadWriteCloser
-// to the spawned process and as a Dialer that returns itself.
+// Dialers in jsonrpc2_v2 return a ReadWriteCloser that sends and receives
+// information from the server. StdDialer wraps stdin/stdout and implements
+// both io.ReadWriteCloser and a Dialer that returns itself.
 //
-// NOTE: Dial should only be called once. This is because closing CmdDialer also
-// kills the underlying process
+// NOTE: Dial should be called once.
@@
-// CmdDialer.Dial returns itself as a CmdDialer is a ReadWriteCloser.
+// Dial returns itself as StdDialer implements io.ReadWriteCloser.

Also applies to: 49-52


22-23: Constructor doc string references CmdDialer; update to StdDialer.

-// Create a new CmdDialer
+// NewStdDialer creates a new StdDialer wrapping the provided streams.
lsp/base_service_client/base_service_client.go (2)

213-216: Injected RPC has no inbound handler — known tech debt.

With c.RPC provided, sc.Handle won’t process notifications unless the provided connection was created with the handler. Tracked for follow‑up; just calling it out.

Based on learnings


211-211: Fixed 5s sleep remains; tracked in follow‑up.

The post‑Dial sleep is still present; issue #926 tracks replacing it with a real handshake/retry.

provider/grpc/socket/uds.go (1)

16-25: Fix TOCTOU race in socket path generation.

Creating a temp file, removing it, then binding is racy. Generate a unique directory and place the socket inside.

@@
-import (
+import (
 	"context"
 	"fmt"
 	"net"
 	"os"
+	"path/filepath"
@@
-func GetAddress(name string) (string, error) {
-	f, err := os.CreateTemp("", fmt.Sprintf("provider-%v-*.sock", name))
-	if err != nil {
-		return "", err
-	}
-	f.Close()
-	os.Remove(f.Name())
-	return f.Name(), nil
-}
+func GetAddress(name string) (string, error) {
+	dir, err := os.MkdirTemp("", fmt.Sprintf("provider-%s-", name))
+	if err != nil {
+		return "", err
+	}
+	// Caller is responsible for cleanup of dir/socket when done.
+	return filepath.Join(dir, "provider.sock"), nil
+}
provider/grpc/socket/pipe_windows.go (1)

45-53: Remove debug print and validate target format in custom dialer.

Avoid stdout noise; return clear errors on bad inputs.

-func DialWindowsPipePassthrough(ctx context.Context, connectionString string) (net.Conn, error) {
-	pipeName, _ := strings.CutPrefix(connectionString, "unix://")
-	pipe, err := winio.DialPipeContext(ctx, pipeName)
+func DialWindowsPipePassthrough(ctx context.Context, connectionString string) (net.Conn, error) {
+	pipeName, ok := strings.CutPrefix(connectionString, "unix://")
+	if !ok || pipeName == "" {
+		return nil, fmt.Errorf("unexpected pipe target: %q; want unix://<pipe>", connectionString)
+	}
+	pipe, err := winio.DialPipeContext(ctx, pipeName)
 	if err != nil {
 		return nil, err
 	}
-	fmt.Printf("pipe: %#v", pipe)
 	return pipe, nil
 }
🧹 Nitpick comments (11)
lsp/base_service_client/base_handlers.go (1)

77-80: Consider hashing params for large payloads.

Using string(req.Params) as part of the map key works but may be inefficient for large request payloads, both in terms of memory and map lookup performance. For requests with substantial parameters, consider hashing the params instead.

Example using a hash:

import "hash/fnv"

type requestKey struct {
	method     string
	paramsHash uint64
}

func hashParams(params []byte) uint64 {
	h := fnv.New64a()
	h.Write(params)
	return h.Sum64()
}

// In Handle():
requestKey := requestKey{
	method:     req.Method,
	paramsHash: hashParams(req.Params),
}
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (2)

73-76: Remove stderr prints; use structured logging.

Drop fmt.Fprintf(os.Stderr, ...) and rely on logr; also remove now‑unused os import.

@@
-import (
-	"context"
-	"fmt"
-	"os"
+import (
+	"context"
+	"fmt"
@@
-	fmt.Fprintf(os.Stderr, "started generic provider init")
+	log.V(2).Info("starting generic provider init")
@@
-		log.Error(err, "ctor error")
-		fmt.Fprintf(os.Stderr, "ctor blah")
+		log.Error(err, "ctor error")

Also applies to: 103-107


41-45: Avoid storing context.TODO() in struct.

Either accept a ctx in NewGenericProvider or use context.Background(); if unused, drop the field.

lsp/base_service_client/std_dialer.go (1)

50-51: Optional: enforce single Dial.

If “Dial once” is required, add a sync.Once or an atomic flag to prevent multiple Dial calls.

provider/grpc/socket/uds.go (1)

31-33: Optional: handle stale socket files and set perms if needed.

If the process may restart, consider removing a stale socket before Listen and adjusting permissions post‑create (os.Chmod) per your access model.

provider/internal/grpc/library.proto (1)

23-23: Clarify languageServerPipe semantics.

Add a comment stating expected format (absolute UDS path on Unix; named pipe on Windows; or connection string?). Prevent misuse.

external-providers/generic-external-provider/main.go (1)

30-30: Use explicit log level and exit cleanly (avoid panic).

  • Replace Level(20) with a defined logrus level (e.g., TraceLevel) for clarity and consistency with other providers.
  • Prefer os.Exit(1) over panic(1) to avoid stack traces in a normal CLI error path.
-	logrusLog.SetLevel(logrus.Level(20))
+	logrusLog.SetLevel(logrus.TraceLevel)
@@
-		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
-		panic(1)
+		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
+		os.Exit(1)

Also applies to: 56-59

external-providers/yq-external-provider/main.go (1)

36-39: Exit instead of panic on invalid startup configuration.

Panic emits a stack trace for a user error. Exit non-zero after logging.

-		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
-		panic(1)
+		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
+		os.Exit(1)
external-providers/java-external-provider/main.go (1)

41-43: Use os.Exit for expected CLI errors.

Replace panic with a clean exit after logging.

-		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
-		panic(1)
+		log.Error(fmt.Errorf("no serving location"), "port or socket must be set.")
+		os.Exit(1)
provider/server.go (2)

253-266: Use background context for long‑lived RPC connection.

The RPC client outlives Init; use newCtx to avoid coupling to the request’s lifetime.

-		rpc, err := socket.ConnectRPC(ctx, config.LanguageServerPipe, handler)
+		rpc, err := socket.ConnectRPC(newCtx, config.LanguageServerPipe, handler)

Confirm jsonrpc2.Dial doesn’t retain/cancel with the passed ctx post‑connect; if it does, background context is required. Based on learnings.


103-111: Optional: handle stale UDS files on startup.

When using Unix sockets, a leftover file from a crash can block Listen. Consider removing pre‑existing paths before listen and on shutdown.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4be055b and be1a5ca.

⛔ Files ignored due to path filters (6)
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/yq-external-provider/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • provider/internal/grpc/library.pb.go is excluded by !**/*.pb.go
  • provider/internal/grpc/library_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (39)
  • Dockerfile (0 hunks)
  • Dockerfile.windows (0 hunks)
  • Makefile (2 hunks)
  • debug/debug.Dockerfile (0 hunks)
  • demo.Dockerfile (2 hunks)
  • external-providers/dotnet-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/Dockerfile (2 hunks)
  • external-providers/generic-external-provider/entrypoint.sh (1 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/generic-external-provider/main.go (4 hunks)
  • external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1 hunks)
  • external-providers/java-external-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/main.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/service_client.go (6 hunks)
  • external-providers/yq-external-provider/go.mod (2 hunks)
  • external-providers/yq-external-provider/main.go (3 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go (0 hunks)
  • go.mod (4 hunks)
  • jsonrpc2/backoff_handler.go (0 hunks)
  • jsonrpc2/handler.go (0 hunks)
  • jsonrpc2/jsonrpc2.go (0 hunks)
  • jsonrpc2/rpcerr.go (0 hunks)
  • jsonrpc2/stream.go (0 hunks)
  • jsonrpc2/wire.go (0 hunks)
  • lsp/base_service_client/base_handlers.go (2 hunks)
  • lsp/base_service_client/base_service_client.go (4 hunks)
  • lsp/base_service_client/std_dialer.go (1 hunks)
  • provider/grpc/provider.go (6 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/grpc/socket/consts.go (1 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/internal/grpc/library.proto (2 hunks)
  • provider/provider.go (3 hunks)
  • provider/server.go (9 hunks)
  • provider_container_settings.json (3 hunks)
  • provider_pod_local_settings.json (1 hunks)
💤 Files with no reviewable changes (10)
  • jsonrpc2/jsonrpc2.go
  • jsonrpc2/rpcerr.go
  • Dockerfile.windows
  • jsonrpc2/stream.go
  • jsonrpc2/backoff_handler.go
  • jsonrpc2/handler.go
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go
  • debug/debug.Dockerfile
  • Dockerfile
  • jsonrpc2/wire.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go
  • Makefile
  • provider/grpc/socket/consts.go
  • provider_pod_local_settings.json
  • provider/grpc/service_client.go
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go
  • go.mod
  • external-providers/generic-external-provider/Dockerfile
  • external-providers/generic-external-provider/entrypoint.sh
  • external-providers/dotnet-external-provider/main.go
  • provider_container_settings.json
  • external-providers/yq-external-provider/go.mod
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-21T16:27:59.216Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/server.go:251-259
Timestamp: 2025-10-21T16:27:59.216Z
Learning: In provider/server.go, the Init method uses context.Background() for creating the RPC client via GetProviderRPCClient because this RPC client is stored in the InitConfig and used across multiple subsequent requests (Evaluate, GetDependencies, etc.). The RPC client's lifecycle is managed separately through explicit Stop calls, not tied to the Init request's context.

Applied to files:

  • provider/server.go
📚 Learning: 2025-10-21T16:25:40.903Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: lsp/base_service_client/base_service_client.go:212-214
Timestamp: 2025-10-21T16:25:40.903Z
Learning: In `lsp/base_service_client/base_service_client.go`, when an RPC connection is provided via `c.RPC` (not nil), it is treated as already initialized and no handler setup is performed. This is known technical debt that needs to be addressed in the future to ensure LSP notifications like `textDocument/publishDiagnostics` are properly processed.

Applied to files:

  • lsp/base_service_client/base_service_client.go
📚 Learning: 2025-07-28T15:28:31.377Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-28T15:27:30.233Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:28-37
Timestamp: 2025-07-28T15:27:30.233Z
Learning: In Windows named pipe gRPC connections, the "unix://" URI scheme prefix must be used instead of a more semantically correct scheme like "pipe://" because it's required for the gRPC package to provide the desired behavior. This is an intentional design choice despite being semantically incorrect.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
🧬 Code graph analysis (12)
lsp/base_service_client/base_handlers.go (2)
jsonrpc2_v2/jsonrpc2.go (2)
  • Handler (60-75)
  • ErrNotHandled (24-24)
jsonrpc2_v2/messages.go (1)
  • Request (30-38)
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)
provider/provider.go (1)
  • BaseClient (450-454)
provider/provider.go (1)
jsonrpc2_v2/conn.go (1)
  • AsyncCall (375-381)
provider/grpc/socket/uds.go (5)
provider/grpc/socket/pipe_windows.go (5)
  • GetAddress (19-24)
  • GetConnectionString (26-28)
  • Listen (41-43)
  • ConnectGRPC (30-39)
  • ConnectRPC (55-66)
jsonrpc2_v2/serve.go (3)
  • Listener (18-31)
  • Dialer (34-37)
  • Dial (57-64)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
jsonrpc2_v2/net.go (1)
  • NetDialer (62-68)
lsp/base_service_client/std_dialer.go (1)
jsonrpc2_v2/serve.go (2)
  • Dialer (34-37)
  • Dial (57-64)
provider/server.go (6)
provider/provider.go (2)
  • BaseClient (450-454)
  • Client (445-448)
jsonrpc2_v2/serve.go (1)
  • Listener (18-31)
provider/grpc/socket/pipe_windows.go (2)
  • Listen (41-43)
  • ConnectRPC (55-66)
provider/grpc/socket/uds.go (2)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
external-providers/generic-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
provider/grpc/provider.go (5)
provider/provider.go (1)
  • Config (88-98)
provider/internal/grpc/library.pb.go (3)
  • Config (78-89)
  • Config (102-102)
  • Config (117-119)
provider/grpc/socket/pipe_windows.go (3)
  • GetAddress (19-24)
  • GetConnectionString (26-28)
  • ConnectGRPC (30-39)
provider/grpc/socket/uds.go (3)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
external-providers/java-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
external-providers/yq-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
lsp/base_service_client/base_service_client.go (5)
provider/provider.go (1)
  • RPCClient (161-165)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (1)
  • NewChainHandler (36-48)
lsp/base_service_client/cmd_dialer.go (1)
  • NewCmdDialer (25-55)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
provider/grpc/socket/pipe_windows.go (5)
provider/grpc/socket/uds.go (5)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/serve.go (2)
  • Listener (18-31)
  • Dial (57-64)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
jsonrpc2_v2/conn.go (2)
  • Connection (66-79)
  • ConnectionOptions (46-60)
⏰ 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). (4)
  • GitHub Check: test
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (macos-latest, mac)
  • GitHub Check: test (windows-latest)
🔇 Additional comments (20)
demo.Dockerfile (1)

15-15: Package additions are reasonable for a demo image.

Adding vim and procps supports interactive debugging and process introspection. While vim adds some image size, it's acceptable for a demo Dockerfile. No changes needed here.

external-providers/java-external-provider/pkg/java_external_provider/service_client.go (5)

24-24: LGTM! Import updated to jsonrpc2_v2.

The import alias maintains code compatibility while migrating to the newer API.


72-72: LGTM! Simplified condition is correct.

The explicit nil check is unnecessary since len(nil) returns 0 in Go.


254-254: LGTM! RPC call migrated correctly to Await pattern.

The call properly uses the passed-in context and maintains existing error handling.


312-312: LGTM! Shutdown RPC call migrated correctly.

The call uses the properly managed shutdownCtx with its cancel deferred at line 295.


411-412: LGTM! Initialize RPC call migrated correctly.

The call properly uses .Await() within the retry loop and maintains existing error handling logic.

external-providers/java-external-provider/go.mod (3)

24-24: Cross-platform socket support dependency confirmed.

The addition of github.com/Microsoft/go-winio v0.6.2 aligns well with the PR's socket communication objectives. This dependency is essential for named-pipe support on Windows, enabling cross-platform UDS/named-pipe compatibility alongside the Unix domain socket support already available on Linux/macOS.


8-49: Coordinated dependency updates look appropriate.

The updates across otel packages (v1.34.0 → v1.35.0), golang.org utilities (net, sys, text), and other transitive dependencies are conservative patch/minor-level bumps. The changes are consistent with the broader socket communication infrastructure being added in this PR.


13-13: gRPC indirect marking is correct and intentional.

Verification confirms:

  1. No direct google.golang.org/grpc imports in java-external-provider source files
  2. gRPC is transitively required through github.com/konveyor/analyzer-lsp (a direct dependency listed in the require block)
  3. The // indirect comment correctly indicates the dependency is not directly imported by this provider
lsp/base_service_client/base_handlers.go (1)

3-11: LGTM!

The new imports appropriately support the BackoffHandler's exponential backoff calculation, synchronization, and timing requirements.

external-providers/generic-external-provider/go.mod (2)

16-16: Addition of go-winio aligns with socket communication feature.

The new dependency github.com/Microsoft/go-winio v0.6.2 is appropriate for supporting cross-platform socket communication (Windows named pipes). This addition directly supports the PR objective of adding socket-based communication across platforms.


27-37: Version compatibility and security advisories verified—no critical issues found.

The updated dependencies are indeed coordinated from the root analyzer-lsp module through the replace directive. Verification confirms:

  • All versions are compatible (go mod tidy succeeds with no errors)
  • No known security vulnerabilities (CVEs) in gRPC v1.73.0, protobuf v1.36.6, or OpenTelemetry v1.35.0
  • Deprecated packages (Jaeger exporter v1.17.0, golang/protobuf v1.5.4) are indirect dependencies only

The current versions are stable and reasonably recent; while newer versions exist (OpenTelemetry v1.38.0, gRPC v1.76.0, protobuf v1.36.10), they offer no security advantage for this codebase.

external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)

33-33: API change from concrete type to interface is safe — no callers require concrete type.

Verification confirms all 5 callers (main.go, and 4 test files) use the returned provider.BaseClient interface without assuming or casting to the concrete *genericProvider type. No type assertions or reflection on the concrete type were found.

provider/provider.go (2)

92-92: Config.UseSocket: clarify precedence with Address.

If both Address and UseSocket/pipe are set, what wins? Recommend documenting/validating to avoid ambiguous startup.


162-165: RPCClient interface changes are properly implemented.

Verification confirms that the Connection type in jsonrpc2_v2/conn.go fully implements the RPCClient interface with all required methods and compatible signatures:

  • Call (line 315) returns *AsyncCall, which resolves to *jsonrpc2.AsyncCall via the package alias
  • Notify (line 263) implements the required signature
  • Close (line 495) implements the required signature

Connection is returned from jsonrpc2.Dial() calls (e.g., uds.go:50, pipe_windows.go:61) where RPCClient is expected. No other implementers were found that require updating, and the code compiles without errors.

provider/grpc/socket/pipe_windows.go (1)

5-17: Seed randomness for pipe name generation to avoid deterministic collisions.

math/rand’s global RNG is unseeded; first values repeat across runs. Seed or use a local RNG.

 import (
 	"context"
 	"fmt"
 	"io"
+	"time"
 	"math/rand"
 	"net"
 	"strings"
@@
 func GetAddress(name string) (string, error) {
-	randInt := rand.Int()
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	randInt := r.Int()
 	pipe_name := fmt.Sprintf("\\\\.\\pipe\\%s-%v", name, randInt)
 	return pipe_name, nil
 }

Also applies to: 19-24

⛔ Skipped due to learnings
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.
provider/grpc/provider.go (4)

14-14: LGTM!

The socket package import is necessary for the new socket-based communication functionality.


44-44: LGTM!

Logger propagation to the start function improves observability and debugging capabilities.


194-206: LGTM with observation.

The logging of provider configuration improves debuggability. The field names match the protobuf definitions.

Note: The typo "Initalized" (should be "Initialized") exists in the protobuf definition (provider/internal/grpc/library.pb.go line 85), not introduced here.


347-353: LGTM!

Extracting LogProviderOut as a standalone function with an explicit logger parameter improves modularity and testability.

@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch from be1a5ca to e60778e Compare October 22, 2025 15:32
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 (16)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

497-506: Critical: Cleanup missing on Dial failure—orphaned LSP process and open pipes.

When dialer creation or jsonrpc2.Dial fails, the spawned child process (lines 462-488) continues running and stdin/stdout pipes remain open, leaking resources.

Apply this diff to ensure cleanup on error:

 // Create a shared input,ouput dialer
 dialer := base.NewStdDialer(stdin, stdout)

 rpc, err := jsonrpc2.Dial(ctx, dialer, jsonrpc2.ConnectionOptions{
 	Handler: base.NewChainHandler(base.LogHandler(log)),
 })
 if err != nil {
 	log.Error(err, "unable to connect over new package")
+	cancelFunc()
+	_ = cmd.Process.Kill()
 	return nil, additionalBuiltinConfig, err
 }
provider/provider.go (1)

155-158: Major: Typo in public API field "Initalized" will leak to YAML/JSON.

The field name and struct tags are misspelled as Initalized (should be Initialized). This becomes part of the public configuration API and will cause confusion.

Apply this diff to fix:

 PipeName string `yaml:"pipeName" json:"pipeName"`

 // Given a pipe name for the init config, we will use that pipe and connect to an already inited provider.
-Initalized bool `yaml:"initalized" json:"initalized"`
+Initialized bool `yaml:"initialized" json:"initialized"`

Then update all usages (e.g., in provider/server.go) to reference .Initialized.

lsp/base_service_client/std_dialer.go (3)

11-16: Minor: Documentation incorrectly references "CmdDialer" instead of "StdDialer".

The type comment references "CmdDialer" three times (lines 12, 15) but the actual type is StdDialer. Additionally, the comment claims "closing CmdDialer also kills the underlying process," but this implementation simply wraps existing I/O streams without managing any process.

Apply this diff:

-// Dialers in jsonrpc2_v2 return a ReadWriteCloser that sends and receives
-// information from the server. CmdDialer functions as a both a ReadWriteCloser
-// to the spawned process and as a Dialer that returns itself.
+// StdDialer wraps stdin/stdout streams and implements both io.ReadWriteCloser
+// and a Dialer interface that returns itself.
 //
-// NOTE: Dial should only be called once. This is because closing CmdDialer also
-// kills the underlying process
+// NOTE: Dial should only be called once.
 type StdDialer struct {

22-30: Minor: Documentation references incorrect type name.

Line 22 says "Create a new CmdDialer" but should reference "StdDialer".

Apply this diff:

-// Create a new CmdDialer
+// NewStdDialer creates a new StdDialer wrapping the provided stdin/stdout streams.
 func NewStdDialer(stdin io.WriteCloser, stdout io.ReadCloser) jsonrpc2.Dialer {

49-52: Minor: Documentation references incorrect type name.

Line 49 says "CmdDialer.Dial" but should reference "StdDialer".

Apply this diff:

-// CmdDialer.Dial returns itself as a CmdDialer is a ReadWriteCloser.
+// Dial returns itself as a StdDialer is a ReadWriteCloser.
 func (rwc *StdDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) {
demo.Dockerfile (1)

1-2: Critical: Local-only base image breaks reproducibility.

The base image localhost/testing/analyzer-lsp:testing-socket references a local image that won't exist in CI/CD pipelines or when the repository is cloned elsewhere, preventing the demo image from being built.

Either:

  • Push to a public registry and update: quay.io/konveyor/analyzer-lsp:testing-socket
  • Revert to the original: quay.io/konveyor/analyzer-lsp
  • Remove this change if it's temporary development work
-#FROM quay.io/konveyor/analyzer-lsp
-FROM localhost/testing/analyzer-lsp:testing-socket
+FROM quay.io/konveyor/analyzer-lsp:testing-socket
external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1)

190-191: Fix context leak on timeout.

The context created with context.WithTimeout is not canceled, which leaks timers and resources.

Apply this diff to capture and defer the cancel function:

-	timeOutCtx, _ := context.WithTimeout(ctx, timeout)
-	err = p.rpc.Call(timeOutCtx, "workspace/executeCommand", wsp).Await(timeOutCtx, &refs)
+	timeOutCtx, cancel := context.WithTimeout(ctx, timeout)
+	defer cancel()
+	err = p.rpc.Call(timeOutCtx, "workspace/executeCommand", wsp).Await(timeOutCtx, &refs)
provider/grpc/socket/pipe_windows.go (1)

45-53: Remove debug print and add error handling.

The function has two issues:

  1. Line 51: Debug print statement should be removed from production code
  2. Line 46: No validation when CutPrefix fails or when pipeName is empty

Apply this diff to improve error handling and remove debug output:

 func DialWindowsPipePassthrough(ctx context.Context, connectionString string) (net.Conn, error) {
-	pipeName, _ := strings.CutPrefix(connectionString, "unix://")
+	pipeName, ok := strings.CutPrefix(connectionString, "unix://")
+	if !ok {
+		return nil, fmt.Errorf("unexpected connection string for windows pipe: %q; expected unix://<pipe>", connectionString)
+	}
+	if pipeName == "" {
+		return nil, fmt.Errorf("empty pipe name in connection string: %q", connectionString)
+	}
 	pipe, err := winio.DialPipeContext(ctx, pipeName)
 	if err != nil {
 		return nil, err
 	}
-	fmt.Printf("pipe: %#v", pipe)
 	return pipe, nil
 }
provider/grpc/socket/uds.go (1)

16-24: Race condition in socket address generation.

Creating a temporary file, removing it, and returning the path creates a window where another process could create a file or socket with the same name before this socket is bound.

Consider generating a unique name without creating/removing the file:

 func GetAddress(name string) (string, error) {
-	f, err := os.CreateTemp("", fmt.Sprintf("provider-%v-*.sock", name))
-	if err != nil {
-		return "", err
-	}
-	f.Close()
-	os.Remove(f.Name())
-	return f.Name(), nil
+	tempDir := os.TempDir()
+	randSuffix := make([]byte, 8)
+	if _, err := rand.Read(randSuffix); err != nil {
+		return "", fmt.Errorf("failed to generate random suffix: %w", err)
+	}
+	sockPath := filepath.Join(tempDir, fmt.Sprintf("provider-%s-%x.sock", name, randSuffix))
+	return sockPath, nil
 }

Don't forget to add the necessary imports:

 import (
+	"crypto/rand"
 	"context"
+	"encoding/hex"
 	"fmt"
 	"net"
 	"os"
+	"path/filepath"
provider/grpc/provider.go (2)

264-303: Critical: Socket connection logic fails for TCP addresses.

The current implementation unconditionally calls socket.ConnectGRPC(connectionString) on line 298 for both socket-based and TCP-based connections. However, socket.ConnectGRPC is designed for socket connections (using custom dialers on Windows) and may not handle raw TCP addresses like "localhost:1234" correctly.

Apply this diff to fix the connection logic and related issues:

 		connectionString = fmt.Sprintf("localhost:%v", port)
 		cmd = exec.CommandContext(ctx, config.BinaryPath, "--port", fmt.Sprintf("%v", port), "--name", name)
 	}
 	// TODO: For each output line, log that line here, allows the server's to output to the main log file. Make sure we name this correctly
 	// cmd will exit with the ending of the ctx.
 	out, err := cmd.StdoutPipe()
 	if err != nil {
 		return nil, nil, err
 	}
 
-	fmt.Printf("\ncommand: %v\n", cmd)
+	log.V(3).Info("starting provider binary", "command", cmd.String())
 	if out != nil {
 		go LogProviderOut(context.Background(), out, log)
 	}
 
 	err = cmd.Start()
 	if err != nil {
 		return nil, nil, err
 	}
 
-	conn, err := socket.ConnectGRPC(connectionString)
+	var conn *grpc.ClientConn
+	if config.UseSocket {
+		conn, err = socket.ConnectGRPC(connectionString)
+	} else {
+		conn, err = grpc.NewClient(connectionString,
+			grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(socket.MAX_MESSAGE_SIZE)),
+			grpc.WithTransportCredentials(insecure.NewCredentials()),
+		)
+	}
 
 	if err != nil {
 		log.Error(err, "did not connect")
+		return nil, nil, err
 	}
 	return conn, out, nil

Additional fixes:

  • Line 288: Replace debug fmt.Printf with structured logging
  • Line 301-302: Add explicit error return after logging connection failure

317-342: Fix error handling in TLS connection paths.

Both TLS connection paths (with and without JWT) log errors but still return conn, nil, nil on failure instead of propagating the error. This causes connection failures to be silently ignored.

Apply this diff:

 		if config.JWTToken == "" {
 			conn, err := grpc.NewClient(fmt.Sprintf(config.Address),
 				grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(socket.MAX_MESSAGE_SIZE)),
 				grpc.WithTransportCredentials(creds))
 			if err != nil {
 				log.Error(err, "did not connect")
+				return nil, nil, err
 			}
 			return conn, nil, nil
 
 		} else {
 			i := &jwtTokeInterceptor{
 				Token: config.JWTToken,
 			}
 			conn, err := grpc.NewClient(fmt.Sprintf(config.Address),
 				grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(socket.MAX_MESSAGE_SIZE)),
 				grpc.WithTransportCredentials(creds), grpc.WithUnaryInterceptor(i.unaryInterceptor))
 			if err != nil {
 				log.Error(err, "did not connect")
+				return nil, nil, err
 			}
 			return conn, nil, nil
 
 		}
lsp/base_service_client/base_handlers.go (5)

118-126: Fix misleading comment block.

Docs reference a logging handler, not this backoff handler.

Apply:

-// Log is invoked for all messages flowing through a Conn.
-// direction indicates if the message being received or sent
-// id is the message id, if not set it was a notification
-// elapsed is the time between a call being seen and the response, and is
-// negative for anything that is not a response.
-// method is the method name specified in the message
-// payload is the parameters for a call or notification, and the result for a
-// response
-// Request is called near the start of processing any request.
+// Handle applies exponential backoff per (method, params) key.
+// It computes a delay (cap 5m), sleeps with context awareness, then returns
+// jsonrpc2.ErrNotHandled to continue the chain. Backoff resets after ~1m idle.

82-86: Fix data race in backoffTimer and correct reset window.

Concurrent Handle() calls can race on retries/last* fields; lastAttemptedTime isn’t updated per call, so the 1m reset window is wrong. Lock the timer and record now every time.

Apply:

 type backoffTimer struct {
-	retries           float64
-	lastAttemptedTime *time.Time
-	lastDurationTime  time.Duration
+	mu                sync.Mutex
+	retries           float64
+	lastAttemptedTime *time.Time
+	lastDurationTime  time.Duration
 }
 
 func (b *backoffTimer) BackoffRequest() time.Duration {
-	if b.lastAttemptedTime == nil {
-		t := time.Now()
-		b.lastAttemptedTime = &t
-		b.lastDurationTime = time.Duration(0)
-		return b.lastDurationTime
-	}
+	b.mu.Lock()
+	defer b.mu.Unlock()
+	now := time.Now()
+	if b.lastAttemptedTime == nil {
+		b.lastAttemptedTime = &now
+		b.lastDurationTime = 0
+		return 0
+	}
 
 	// if backoff exists but has been more than a minute passed the
 	// the last back off, then reset the backoff.
-	if time.Now().After(b.lastAttemptedTime.Add(b.lastDurationTime).Add(time.Minute * 1)) {
+	if now.After(b.lastAttemptedTime.Add(b.lastDurationTime).Add(time.Minute)) {
 		b.retries = 0
-		t := time.Now()
-		b.lastAttemptedTime = &t
-		b.lastDurationTime = time.Duration(0)
-		return b.lastDurationTime
+		b.lastAttemptedTime = &now
+		b.lastDurationTime = 0
+		return 0
 	}
 
 	// calculated back off
-	b.lastDurationTime = time.Second * time.Duration((math.Pow(2, b.retries)))
+	b.lastDurationTime = time.Second * time.Duration(math.Pow(2, b.retries))
 
 	// Cap back off at 5 min.
 	if b.lastDurationTime >= (time.Minute * 5) {
 		b.lastDurationTime = time.Minute * 5
 	}
 
-	b.retries = b.retries + 1
+	b.retries++
+	b.lastAttemptedTime = &now
 	return b.lastDurationTime
 }

Also applies to: 88-116


145-149: Honor context cancellation; replace blind sleep.

time.Sleep(d) can block up to 5m even after ctx.Done().

Apply:

-	d := backOff.BackoffRequest()
-	b.logger.V(9).Info("starting backing off request", "method", req.Method, "duration", d)
-	time.Sleep(d)
-	b.logger.V(9).Info("stopping backing off request", "method", req.Method)
-	return nil, jsonrpc2.ErrNotHandled
+	d := backOff.BackoffRequest()
+	if d > 0 {
+		b.logger.V(9).Info("starting backoff", "method", req.Method, "duration", d)
+		timer := time.NewTimer(d)
+		defer timer.Stop()
+		select {
+		case <-ctx.Done():
+			b.logger.V(9).Info("backoff canceled", "method", req.Method)
+			return nil, jsonrpc2.ErrNotHandled
+		case <-timer.C:
+		}
+		b.logger.V(9).Info("finished backoff", "method", req.Method)
+	}
+	return nil, jsonrpc2.ErrNotHandled

63-75: Prevent unbounded growth of failedRequests.

Entries are never evicted; memory can grow without bound.

Suggested lazy, low-risk cleanup:

 func NewBackoffHandler(log logr.Logger) jsonrpc2.Handler {
-	return &BackoffHandler{
+	h := &BackoffHandler{
 		failedRequests:   make(map[requestKey]*backoffTimer),
 		failedRequestsMu: sync.Mutex{},
 		logger:           log,
 	}
+	// Optional: background pruning every 5m; make stoppable if you add a Close().
+	go h.cleanupOldEntries()
+	return h
-}
+}
+
+func (b *BackoffHandler) cleanupOldEntries() {
+	t := time.NewTicker(5 * time.Minute)
+	defer t.Stop()
+	for range t.C {
+		cutoff := time.Now().Add(-10 * time.Minute)
+		b.failedRequestsMu.Lock()
+		for k, v := range b.failedRequests {
+			if v.lastAttemptedTime != nil && v.lastAttemptedTime.Before(cutoff) {
+				delete(b.failedRequests, k)
+			}
+		}
+		b.failedRequestsMu.Unlock()
+	}
+}

Alternatively, use a bounded LRU/TTL cache.


133-149: Backoff throttles all requests, not just failures.

A timer entry is created on first sight and backoff increases on every subsequent call, even if downstream succeeds. This unnecessarily delays healthy traffic.

Minimal behavior fix: only back off when a prior failure was recorded; don’t auto-create entries here.

-	b.failedRequestsMu.Lock()
-	backOff, ok := b.failedRequests[requestKey]
-	if !ok {
-		backOff = &backoffTimer{retries: 0, lastAttemptedTime: nil, lastDurationTime: 0}
-		b.failedRequests[requestKey] = backOff
-	}
-	b.failedRequestsMu.Unlock()
+	b.failedRequestsMu.Lock()
+	backOff, ok := b.failedRequests[requestKey]
+	b.failedRequestsMu.Unlock()
+	if !ok {
+		// No prior failure recorded -> no backoff
+		return nil, jsonrpc2.ErrNotHandled
+	}

Add recording APIs and call them at failure/success sites:

// New: record a failure
func (b *BackoffHandler) RecordFailure(method string, params []byte) {
	key := requestKey{method: method, params: string(params)}
	b.failedRequestsMu.Lock()
	defer b.failedRequestsMu.Unlock()
	if _, ok := b.failedRequests[key]; !ok {
		b.failedRequests[key] = &backoffTimer{}
	}
}

// New: reset on success
func (b *BackoffHandler) Reset(method string, params []byte) {
	key := requestKey{method: method, params: string(params)}
	b.failedRequestsMu.Lock()
	delete(b.failedRequests, key)
	b.failedRequestsMu.Unlock()
}

If changing constructor type is disruptive, add a parallel constructor returning *BackoffHandler for wiring these calls.

Run to locate call sites to wire failure/success hooks:

#!/bin/bash
rg -nP -C3 'NewBackoffHandler\(|BackoffHandler\)' 
🧹 Nitpick comments (3)
provider/grpc/socket/consts.go (1)

1-5: Optional: Consider idiomatic Go naming convention.

The constant MAX_MESSAGE_SIZE (8MB) is functionally correct, but Go convention typically uses MaxMessageSize for exported constants rather than all-caps.

If you prefer idiomatic Go style, apply this diff:

 package socket

 const (
-	MAX_MESSAGE_SIZE = 1024 * 1024 * 8
+	MaxMessageSize = 1024 * 1024 * 8
 )

Then update references in pipe_windows.go, uds.go, and provider/server.go accordingly.

lsp/base_service_client/base_handlers.go (2)

77-80: Reduce key cardinality and copies for params.

string(req.Params) copies entire payload and varies with non-canonical JSON. Consider hashing canonical params or keying by method only (if sufficient).

Example:

func makeKey(method string, params json.RawMessage) requestKey {
	sum := sha256.Sum256(params) // or canonicalize first
	return requestKey{method: method, params: hex.EncodeToString(sum[:])}
}

106-115: Consider adding jitter to backoff.

Deterministic 2^n can cause lockstep retries. Apply ±50% jitter to spread load.

Example:

base := time.Second * time.Duration(math.Pow(2, b.retries))
jitter := time.Duration(rand.Int63n(int64(base))) - base/2
b.lastDurationTime = base + jitter

Seed rand safely at init.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be1a5ca and e60778e.

⛔ Files ignored due to path filters (6)
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/yq-external-provider/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • provider/internal/grpc/library.pb.go is excluded by !**/*.pb.go
  • provider/internal/grpc/library_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (39)
  • Dockerfile (0 hunks)
  • Dockerfile.windows (0 hunks)
  • Makefile (2 hunks)
  • debug/debug.Dockerfile (0 hunks)
  • demo.Dockerfile (2 hunks)
  • external-providers/dotnet-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/Dockerfile (2 hunks)
  • external-providers/generic-external-provider/entrypoint.sh (1 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/generic-external-provider/main.go (4 hunks)
  • external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1 hunks)
  • external-providers/java-external-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/main.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/service_client.go (6 hunks)
  • external-providers/yq-external-provider/go.mod (2 hunks)
  • external-providers/yq-external-provider/main.go (3 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go (0 hunks)
  • go.mod (4 hunks)
  • jsonrpc2/backoff_handler.go (0 hunks)
  • jsonrpc2/handler.go (0 hunks)
  • jsonrpc2/jsonrpc2.go (0 hunks)
  • jsonrpc2/rpcerr.go (0 hunks)
  • jsonrpc2/stream.go (0 hunks)
  • jsonrpc2/wire.go (0 hunks)
  • lsp/base_service_client/base_handlers.go (2 hunks)
  • lsp/base_service_client/base_service_client.go (4 hunks)
  • lsp/base_service_client/std_dialer.go (1 hunks)
  • provider/grpc/provider.go (6 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/grpc/socket/consts.go (1 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/internal/grpc/library.proto (2 hunks)
  • provider/provider.go (3 hunks)
  • provider/server.go (9 hunks)
  • provider_container_settings.json (3 hunks)
  • provider_pod_local_settings.json (1 hunks)
💤 Files with no reviewable changes (10)
  • jsonrpc2/jsonrpc2.go
  • jsonrpc2/rpcerr.go
  • jsonrpc2/wire.go
  • Dockerfile.windows
  • debug/debug.Dockerfile
  • jsonrpc2/backoff_handler.go
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go
  • jsonrpc2/stream.go
  • Dockerfile
  • jsonrpc2/handler.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • provider/internal/grpc/library.proto
  • provider/grpc/service_client.go
  • external-providers/yq-external-provider/main.go
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go
  • external-providers/generic-external-provider/entrypoint.sh
  • lsp/base_service_client/base_service_client.go
  • provider_container_settings.json
  • external-providers/java-external-provider/go.mod
  • external-providers/generic-external-provider/Dockerfile
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-28T15:28:31.377Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-28T15:27:30.233Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:28-37
Timestamp: 2025-07-28T15:27:30.233Z
Learning: In Windows named pipe gRPC connections, the "unix://" URI scheme prefix must be used instead of a more semantically correct scheme like "pipe://" because it's required for the gRPC package to provide the desired behavior. This is an intentional design choice despite being semantically incorrect.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-10-21T16:27:59.216Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/server.go:251-259
Timestamp: 2025-10-21T16:27:59.216Z
Learning: In provider/server.go, the Init method uses context.Background() for creating the RPC client via GetProviderRPCClient because this RPC client is stored in the InitConfig and used across multiple subsequent requests (Evaluate, GetDependencies, etc.). The RPC client's lifecycle is managed separately through explicit Stop calls, not tied to the Init request's context.

Applied to files:

  • provider/server.go
🧬 Code graph analysis (12)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (5)
lsp/base_service_client/std_dialer.go (1)
  • NewStdDialer (23-30)
jsonrpc2_v2/serve.go (1)
  • Dial (57-64)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (2)
  • NewChainHandler (36-48)
  • LogHandler (22-27)
external-providers/java-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)
provider/provider.go (1)
  • BaseClient (450-454)
provider/grpc/socket/uds.go (4)
provider/grpc/socket/pipe_windows.go (5)
  • GetAddress (19-24)
  • GetConnectionString (26-28)
  • Listen (41-43)
  • ConnectGRPC (30-39)
  • ConnectRPC (55-66)
jsonrpc2_v2/serve.go (3)
  • Listener (18-31)
  • Dialer (34-37)
  • Dial (57-64)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/net.go (1)
  • NetDialer (62-68)
lsp/base_service_client/base_handlers.go (2)
jsonrpc2_v2/jsonrpc2.go (2)
  • Handler (60-75)
  • ErrNotHandled (24-24)
jsonrpc2_v2/messages.go (1)
  • Request (30-38)
provider/grpc/socket/pipe_windows.go (5)
provider/grpc/socket/uds.go (5)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/serve.go (2)
  • Listener (18-31)
  • Dial (57-64)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
jsonrpc2_v2/conn.go (2)
  • Connection (66-79)
  • ConnectionOptions (46-60)
external-providers/dotnet-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
external-providers/generic-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
lsp/base_service_client/std_dialer.go (1)
jsonrpc2_v2/serve.go (2)
  • Dialer (34-37)
  • Dial (57-64)
provider/provider.go (1)
jsonrpc2_v2/conn.go (1)
  • AsyncCall (375-381)
provider/grpc/provider.go (5)
provider/provider.go (1)
  • Config (88-98)
provider/internal/grpc/library.pb.go (3)
  • Config (78-89)
  • Config (102-102)
  • Config (117-119)
provider/grpc/socket/pipe_windows.go (3)
  • GetAddress (19-24)
  • GetConnectionString (26-28)
  • ConnectGRPC (30-39)
provider/grpc/socket/uds.go (3)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
provider/server.go (5)
provider/provider.go (2)
  • BaseClient (450-454)
  • Client (445-448)
provider/grpc/socket/pipe_windows.go (2)
  • Listen (41-43)
  • ConnectRPC (55-66)
provider/grpc/socket/uds.go (2)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
⏰ 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). (4)
  • GitHub Check: benchmark (macos-latest, mac)
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: test
  • GitHub Check: test (windows-latest)
🔇 Additional comments (26)
external-providers/yq-external-provider/go.mod (1)

16-41: LGTM—Routine dependency updates aligned with cross-platform socket support.

The addition of github.com/Microsoft/go-winio enables Windows named pipe support, and the version bumps for OpenTelemetry, gRPC, and related packages are standard maintenance.

go.mod (1)

6-55: LGTM—Dependency updates enable cross-platform socket communication.

The new direct dependency github.com/Microsoft/go-winio v0.6.2 supports Windows named pipes, aligning with the PR's cross-platform socket objectives. The version bumps are routine maintenance.

external-providers/java-external-provider/pkg/java_external_provider/provider.go (2)

22-23: LGTM—Import updates align with jsonrpc2_v2 migration.

The import path change to jsonrpc2_v2 and the base alias support the new dialer-based RPC connection mechanism introduced in this PR.


477-477: LGTM—Improved error visibility during language server retry.

Adding the error log when retrying language server startup enhances observability.

provider/provider.go (3)

18-18: LGTM—Import supports jsonrpc2_v2 migration.

The import path update aligns with the new async RPC pattern and socket-based transport introduced in this PR.


92-92: LGTM—New UseSocket field enables socket-based provider communication.

The boolean flag allows configuration of socket-based vs network-based provider connections.


162-165: LGTM—RPCClient interface updated for async pattern and explicit lifecycle.

The interface changes support the new jsonrpc2_v2 transport:

  • Call now returns *jsonrpc2.AsyncCall for async operations
  • Close() method added for explicit cleanup
  • Parameter type updated from interface{} to any (idiomatic Go 1.18+)

These are intentional breaking changes for the new socket-based RPC mechanism.

lsp/base_service_client/std_dialer.go (1)

32-47: LGTM—Clean I/O delegation and proper error handling.

The Read, Write, and Close methods correctly delegate to the underlying streams, and Close properly combines errors from both streams.

demo.Dockerfile (1)

15-15: LGTM—Additional debugging tools added.

Installing vim and procps provides useful debugging capabilities within the demo container.

provider_pod_local_settings.json (1)

17-18: LGTM—Pipe configuration enables language server communication testing.

The pipeName field aligns with the new InitConfig.PipeName capability. Note that the Unix socket path /tmp/go-pls-pipe-test is platform-specific and won't work on Windows; consider documenting this if this config is reused across platforms.

external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)

33-33: LGTM! Interface return type improves abstraction.

The change from returning a concrete *genericProvider to the provider.BaseClient interface is well-aligned with the socket communication feature, enabling polymorphic usage across different transport mechanisms.

external-providers/java-external-provider/pkg/java_external_provider/service_client.go (3)

24-24: LGTM! Import updated for jsonrpc2_v2 migration.

The import change aligns with the broader refactoring to use the updated jsonrpc2 implementation across providers.


72-74: LGTM! Simplified condition is correct.

The condition simplification maintains the same behavior while being more concise.


191-200: LGTM! RPC calls updated to use Await pattern.

The transition to the .Await() pattern is consistent with the jsonrpc2_v2 API and maintains proper error handling with IsRPCClosed checks.

Also applies to: 254-262, 312-316, 411-420

external-providers/dotnet-external-provider/main.go (1)

21-21: LGTM! Socket support added consistently.

The addition of the --socket flag, updated validation to require either port or socket, and the extended NewServer call are well-aligned with the cross-platform socket communication feature.

Also applies to: 38-40, 59-59

external-providers/generic-external-provider/main.go (1)

17-17: LGTM! Socket support added consistently.

The socket flag, validation logic, and NewServer call updates align well with the socket communication feature.

Also applies to: 56-59, 77-77

external-providers/java-external-provider/main.go (1)

17-17: LGTM! Socket support added consistently.

The socket flag, validation, and NewServer call updates are well-aligned with the cross-platform socket communication feature.

Also applies to: 41-43, 61-61

external-providers/generic-external-provider/go.mod (1)

16-16: LGTM! Dependency updates support socket communication.

The addition of go-winio for Windows named pipe support and the OpenTelemetry/gRPC version bumps align with the socket communication feature requirements.

Also applies to: 27-37

provider/grpc/socket/uds.go (1)

27-33: LGTM! Socket connection functions implemented correctly.

The GetConnectionString, Listen, ConnectGRPC, and ConnectRPC functions properly handle Unix domain socket connections for both gRPC and JSON-RPC transports.

Also applies to: 35-55

provider/grpc/socket/pipe_windows.go (1)

19-24: LGTM! Windows pipe support implemented correctly.

The Windows named pipe functions properly handle pipe address generation, connection string formatting, gRPC client creation, listening, and JSON-RPC connections. The use of "unix://" in the connection string is intentional per project requirements.

Based on learnings

Also applies to: 26-28, 30-43, 55-78

provider/server.go (2)

103-111: LGTM: Socket and TCP listener setup.

The conditional logic correctly handles both TCP port and socket-based listening, with appropriate error handling when neither is configured.


509-511: LGTM: JWT callback signature updated.

The return type change from interface{} to any is a standard Go 1.18+ modernization, as any is an alias for interface{}.

provider/grpc/provider.go (3)

194-207: LGTM: Configuration logging added.

Logging the provider configuration helps with debugging and troubleshooting. The new fields LanguageServerPipe and Initalized are properly wired into the protobuf config.


306-315: LGTM: Insecure address-based connection.

The insecure connection path correctly uses grpc.NewClient with appropriate message size limits and properly returns errors.


347-353: LGTM: Logging improvements.

Converting LogProviderOut to a standalone function with explicit logger parameter and using structured logging (log.V(3).Info) is a good improvement for maintainability.

Makefile (1)

59-59: Entrypoint script verified—no issues found.

The entrypoint script correctly forwards CLI arguments to the provider binary using "$@", and the Dockerfile confirms the script is copied to /usr/local/bin/entrypoint.sh during the build.

The entrypoint is specific to the golang-provider because it starts the Go Language Server (gopls) on a unix socket before launching the provider—a setup that nodejs and python variants do not require. The selective application to only golang-provider is intentional and correct.

* Adding winio from microsoft to use named pipes
* Fixup to use the non-deprecated way of creating clients
* Adding package socket, to encapsulate getting sockets and using them.
  Allows for build parameters to be used so that the right functions are
  called on the correct platforms.

Signed-off-by: Shawn Hurley <[email protected]>
* Adding ability for client and providers to handle new GRPC config
  field `LanguageServerPipe`.
* When this is set, the base server will create a connection to the pipe
  and set in the init config the RPC field.
* Generic Provider and java provider will use this RPC field when set.
* Updated demo with an example of usage for golang provider.

Signed-off-by: Shawn Hurley <[email protected]>
* Fixed up some things that code rabbit found
* added ability for generic service client to continue to have handlers
  even when provider/server creates the connection

Signed-off-by: Shawn Hurley <[email protected]>
@shawn-hurley shawn-hurley force-pushed the feature/adding-socket-communication branch from e60778e to 80be3a3 Compare October 22, 2025 15:59
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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)

73-73: Remove or replace debug statements with structured logging.

The fmt.Fprintf(os.Stderr, ...) statements bypass the provided logr.Logger. Line 105's message "ctor blah" appears to be temporary debug code.

Consider using the logger parameter for consistency:

-	fmt.Fprintf(os.Stderr, "started generic provider init")
+	log.V(1).Info("started generic provider init")
 	if err != nil {
 		log.Error(err, "ctor error")
-		fmt.Fprintf(os.Stderr, "ctor blah")
 		return nil, provider.InitConfig{}, fmt.Errorf("ctor error: %w", err)
 	}

Also applies to: 105-105

provider/server.go (1)

509-519: Enforce JWT signing method to avoid alg confusion.

Verify expected HMAC method before returning the key.

Apply:

- token, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
-   return []byte(s.SecretKey), nil
- })
+ token, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
+   if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
+     return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
+   }
+   return []byte(s.SecretKey), nil
+ })
♻️ Duplicate comments (8)
external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1)

190-191: Fix context leak on timeout.

The cancel function from context.WithTimeout must be captured and called to prevent timer leaks. This issue was previously flagged and remains unresolved.

Apply this diff to fix the leak:

-timeOutCtx, _ := context.WithTimeout(ctx, timeout)
+timeOutCtx, cancel := context.WithTimeout(ctx, timeout)
+defer cancel()
 err = p.rpc.Call(timeOutCtx, "workspace/executeCommand", wsp).Await(timeOutCtx, &refs)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

497-507: Critical: LSP process orphaned on Dial failure.

If jsonrpc2.Dial fails (line 500), the spawned LSP process and its pipes remain open. While cancelFunc() is called (line 504), the child process itself is not terminated and the stdin/stdout pipes are not explicitly closed, leading to resource leaks.

Ensure cleanup by adding:

 dialer := base.NewStdDialer(stdin, stdout)

 rpc, err := jsonrpc2.Dial(ctx, dialer, jsonrpc2.ConnectionOptions{
 	Handler: base.NewChainHandler(base.LogHandler(log)),
 })
 if err != nil {
 	cancelFunc()
+	stdin.Close()
+	stdout.Close()
+	if cmd.Process != nil {
+		_ = cmd.Process.Kill()
+	}
 	log.Error(err, "unable to connect over new package")
 	return nil, additionalBuiltinConfig, err
 }
lsp/base_service_client/base_service_client.go (1)

240-240: Use InitializedParams for "initialized" notification.

The LSP "initialized" notification should send protocol.InitializedParams{}, not protocol.InitializeParams{}.

Apply this diff:

-err = sc.Conn.Notify(sc.Ctx, "initialized", protocol.InitializeParams{})
+err = sc.Conn.Notify(sc.Ctx, "initialized", protocol.InitializedParams{})
lsp/base_service_client/std_dialer.go (1)

19-19: Fix documentation: CmdDialer → StdDialer.

The comment references "CmdDialer" but the actual type is "StdDialer".

Apply this diff:

-// Create a new CmdDialer
+// NewStdDialer creates a new StdDialer wrapping the provided stdin/stdout streams.
 func NewStdDialer(stdin io.WriteCloser, stdout io.ReadCloser) jsonrpc2.Dialer {
provider/grpc/provider.go (2)

288-291: Replace debug print with structured log; always tail stdout.

Apply:

- fmt.Printf("\ncommand: %v\n", cmd)
- if out != nil {
-   go LogProviderOut(context.Background(), out, log)
- }
+ log.V(3).Info("starting provider binary", "command", cmd.String())
+ go LogProviderOut(context.Background(), out, log)

298-303: Critical: return error on failed dial.

Currently logs and returns conn (possibly nil), leading to panics downstream.

Apply:

- conn, err := socket.ConnectGRPC(connectionString)
-
- if err != nil {
-   log.Error(err, "did not connect")
- }
- return conn, out, nil
+ conn, err := socket.ConnectGRPC(connectionString)
+ if err != nil {
+   log.Error(err, "did not connect")
+   return nil, nil, err
+ }
+ return conn, out, nil
provider/grpc/socket/pipe_windows.go (2)

54-61: Remove debug print and validate target prefix.

Apply:

- pipeName, _ := strings.CutPrefix(connectionString, "unix://")
+ pipeName, ok := strings.CutPrefix(connectionString, "unix://")
+ if !ok || pipeName == "" {
+   return nil, fmt.Errorf("unexpected windows pipe target: %q; want unix://<pipe>", connectionString)
+ }
   pipe, err := winio.DialPipeContext(ctx, pipeName)
   if err != nil {
     return nil, err
   }
- fmt.Printf("pipe: %#v", pipe)
   return pipe, nil

19-24: Unseeded math/rand can cause repeat pipe names across runs.

Seed once to avoid deterministic collisions.

Apply:

 import (
   "context"
   "fmt"
   "io"
   "math/rand"
   "net"
   "strings"
+  "time"
 
   "github.com/Microsoft/go-winio"
   // ...
 )
 
+func init() {
+  rand.Seed(time.Now().UnixNano())
+}
🧹 Nitpick comments (6)
provider/server.go (4)

103-111: UDS startup: best‑effort cleanup of stale socket.

Before listening on a Unix socket, unlink any stale path to avoid EADDRINUSE on restart.

Apply:

} else if s.SocketPath != "" {
-    listen, err = socket.Listen(s.SocketPath)
+    _ = os.Remove(s.SocketPath) // ignore error if not present
+    listen, err = socket.Listen(s.SocketPath)

121-131: Add max message size limits to TLS server too.

Parity with the non‑TLS branch prevents large messages from being rejected only in TLS.

Apply:

- gs = grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(s.authUnaryInterceptor))
+ gs = grpc.NewServer(
+   grpc.Creds(creds),
+   grpc.UnaryInterceptor(s.authUnaryInterceptor),
+   grpc.MaxRecvMsgSize(socket.MAX_MESSAGE_SIZE),
+   grpc.MaxSendMsgSize(socket.MAX_MESSAGE_SIZE),
+ )
- gs = grpc.NewServer(grpc.Creds(creds))
+ gs = grpc.NewServer(
+   grpc.Creds(creds),
+   grpc.MaxRecvMsgSize(socket.MAX_MESSAGE_SIZE),
+   grpc.MaxSendMsgSize(socket.MAX_MESSAGE_SIZE),
+ )

272-276: Use the server’s RNG instance for ID generation.

Stay consistent with seeded s.rand instead of global rand.

Apply:

- id := rand.Int63()
+ id := s.rand.Int63()

282-287: Stored context likely canceled; prefer long‑lived ctx or drop field.

You store the request ctx; if unused or for lifecycle, store newCtx instead or remove.

Apply:

- ctx:    ctx,
+ ctx:    newCtx,
provider/grpc/provider.go (2)

132-148: Deterministic service wait with ticker + timeout.

Avoid select default randomness; use a ticker and a fixed timeout.

Example:

deadline := time.After(30 * time.Second)
tick := time.NewTicker(3 * time.Second)
defer tick.Stop()
for {
  select {
  case <-tick.C:
    services, err := refClt.ListServices()
    if err == nil && len(services) != 0 { return services, nil }
    if err != nil { log.Error(err, "error for list services retrying") }
  case <-deadline:
    return nil, fmt.Errorf("no services found")
  }
}

48-76: Avoid storing a context that’s canceled before return.

refCltCtx is deferred-canceled; don’t assign it into gp.ctx. Use ctxCmd or a fresh background.

Apply:

- gp := grpcProvider{
+ gp := grpcProvider{
    // ...
-   ctx:            refCltCtx,
+   ctx:            ctxCmd,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e60778e and 80be3a3.

⛔ Files ignored due to path filters (6)
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/java-external-provider/go.sum is excluded by !**/*.sum
  • external-providers/yq-external-provider/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • provider/internal/grpc/library.pb.go is excluded by !**/*.pb.go
  • provider/internal/grpc/library_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • Dockerfile (1 hunks)
  • Dockerfile.windows (0 hunks)
  • Makefile (2 hunks)
  • debug/debug.Dockerfile (0 hunks)
  • demo.Dockerfile (1 hunks)
  • external-providers/dotnet-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/Dockerfile (2 hunks)
  • external-providers/generic-external-provider/entrypoint.sh (1 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/generic-external-provider/main.go (3 hunks)
  • external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1 hunks)
  • external-providers/java-external-provider/go.mod (1 hunks)
  • external-providers/java-external-provider/main.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go (0 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go (3 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/service_client.go (6 hunks)
  • external-providers/yq-external-provider/go.mod (2 hunks)
  • external-providers/yq-external-provider/main.go (3 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go (0 hunks)
  • go.mod (4 hunks)
  • jsonrpc2/backoff_handler.go (0 hunks)
  • jsonrpc2/handler.go (0 hunks)
  • jsonrpc2/jsonrpc2.go (0 hunks)
  • jsonrpc2/rpcerr.go (0 hunks)
  • jsonrpc2/stream.go (0 hunks)
  • jsonrpc2/wire.go (0 hunks)
  • lsp/base_service_client/base_service_client.go (4 hunks)
  • lsp/base_service_client/std_dialer.go (1 hunks)
  • provider/grpc/provider.go (6 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/grpc/socket/consts.go (1 hunks)
  • provider/grpc/socket/pipe_windows.go (1 hunks)
  • provider/grpc/socket/uds.go (1 hunks)
  • provider/internal/grpc/library.proto (2 hunks)
  • provider/provider.go (3 hunks)
  • provider/server.go (9 hunks)
  • provider_container_settings.json (3 hunks)
  • provider_pod_local_settings.json (1 hunks)
💤 Files with no reviewable changes (10)
  • Dockerfile.windows
  • external-providers/java-external-provider/pkg/java_external_provider/dependency_test.go
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go
  • debug/debug.Dockerfile
  • jsonrpc2/backoff_handler.go
  • jsonrpc2/handler.go
  • jsonrpc2/jsonrpc2.go
  • jsonrpc2/rpcerr.go
  • jsonrpc2/stream.go
  • jsonrpc2/wire.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • external-providers/generic-external-provider/main.go
  • provider/provider.go
  • Dockerfile
  • external-providers/yq-external-provider/main.go
  • provider/grpc/service_client.go
  • go.mod
  • provider/grpc/socket/uds.go
  • external-providers/yq-external-provider/go.mod
  • Makefile
  • provider_pod_local_settings.json
  • external-providers/generic-external-provider/Dockerfile
  • demo.Dockerfile
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-10-21T16:25:40.903Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: lsp/base_service_client/base_service_client.go:212-214
Timestamp: 2025-10-21T16:25:40.903Z
Learning: In `lsp/base_service_client/base_service_client.go`, when an RPC connection is provided via `c.RPC` (not nil), it is treated as already initialized and no handler setup is performed. This is known technical debt that needs to be addressed in the future to ensure LSP notifications like `textDocument/publishDiagnostics` are properly processed.

Applied to files:

  • lsp/base_service_client/base_service_client.go
  • provider/server.go
📚 Learning: 2025-07-25T02:05:02.884Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#863
File: external-providers/generic-external-provider/go.mod:3-3
Timestamp: 2025-07-25T02:05:02.884Z
Learning: The `go` directive in go.mod only accepts major.minor (e.g., "go 1.23"); patch versions like "go 1.23.11" are invalid. To require a specific patch version, use the separate `toolchain` directive (introduced in Go 1.21), e.g., `toolchain go1.23.11`.

Applied to files:

  • external-providers/java-external-provider/go.mod
📚 Learning: 2025-07-28T15:28:31.377Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:22-26
Timestamp: 2025-07-28T15:28:31.377Z
Learning: For Windows named pipe address generation in the analyzer-lsp project, cryptographic security is not required. Basic randomness using math/rand is sufficient for collision avoidance in local inter-process communication scenarios.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-07-28T15:27:30.233Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/grpc/socket/pipe_windows.go:28-37
Timestamp: 2025-07-28T15:27:30.233Z
Learning: In Windows named pipe gRPC connections, the "unix://" URI scheme prefix must be used instead of a more semantically correct scheme like "pipe://" because it's required for the gRPC package to provide the desired behavior. This is an intentional design choice despite being semantically incorrect.

Applied to files:

  • provider/grpc/socket/pipe_windows.go
📚 Learning: 2025-10-21T16:27:59.216Z
Learnt from: shawn-hurley
PR: konveyor/analyzer-lsp#860
File: provider/server.go:251-259
Timestamp: 2025-10-21T16:27:59.216Z
Learning: In provider/server.go, the Init method uses context.Background() for creating the RPC client via GetProviderRPCClient because this RPC client is stored in the InitConfig and used across multiple subsequent requests (Evaluate, GetDependencies, etc.). The RPC client's lifecycle is managed separately through explicit Stop calls, not tied to the Init request's context.

Applied to files:

  • provider/server.go
🧬 Code graph analysis (9)
external-providers/dotnet-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
lsp/base_service_client/std_dialer.go (1)
jsonrpc2_v2/serve.go (2)
  • Dialer (34-37)
  • Dial (57-64)
lsp/base_service_client/base_service_client.go (6)
provider/provider.go (1)
  • RPCClient (161-165)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (1)
  • NewChainHandler (33-45)
jsonrpc2_v2/serve.go (2)
  • Dialer (34-37)
  • Dial (57-64)
lsp/base_service_client/cmd_dialer.go (1)
  • NewCmdDialer (25-55)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (5)
lsp/base_service_client/std_dialer.go (1)
  • NewStdDialer (20-27)
jsonrpc2_v2/serve.go (1)
  • Dial (57-64)
jsonrpc2_v2/conn.go (1)
  • ConnectionOptions (46-60)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
lsp/base_service_client/base_handlers.go (2)
  • NewChainHandler (33-45)
  • LogHandler (19-24)
provider/grpc/provider.go (5)
provider/provider.go (1)
  • Config (88-98)
provider/internal/grpc/library.pb.go (3)
  • Config (78-89)
  • Config (102-102)
  • Config (117-119)
provider/grpc/socket/pipe_windows.go (3)
  • GetAddress (19-24)
  • GetConnectionString (26-28)
  • ConnectGRPC (30-48)
provider/grpc/socket/uds.go (3)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
external-providers/java-external-provider/main.go (1)
provider/server.go (1)
  • NewServer (65-100)
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)
provider/provider.go (1)
  • BaseClient (450-454)
provider/grpc/socket/pipe_windows.go (4)
provider/grpc/socket/uds.go (5)
  • GetAddress (16-25)
  • GetConnectionString (27-29)
  • ConnectGRPC (35-42)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/serve.go (2)
  • Listener (18-31)
  • Dial (57-64)
jsonrpc2_v2/conn.go (2)
  • Connection (66-79)
  • ConnectionOptions (46-60)
provider/server.go (5)
provider/provider.go (2)
  • BaseClient (450-454)
  • Client (445-448)
provider/grpc/socket/pipe_windows.go (2)
  • Listen (50-52)
  • ConnectRPC (64-75)
provider/grpc/socket/uds.go (2)
  • Listen (31-33)
  • ConnectRPC (44-55)
provider/grpc/socket/consts.go (1)
  • MAX_MESSAGE_SIZE (4-4)
jsonrpc2_v2/jsonrpc2.go (1)
  • Handler (60-75)
⏰ 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). (3)
  • GitHub Check: test
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: test (windows-latest)
🔇 Additional comments (20)
external-providers/generic-external-provider/pkg/generic_external_provider/provider.go (1)

33-33: LGTM! Returning interface type improves flexibility.

Changing the return type from the concrete *genericProvider to the provider.BaseClient interface follows Go best practices and improves testability and flexibility.

external-providers/java-external-provider/pkg/java_external_provider/service_client.go (5)

24-24: LGTM! Import updated for jsonrpc2_v2 migration.

The import path has been correctly updated to use the new jsonrpc2_v2 package while maintaining the jsonrpc2 alias for compatibility with existing code.


72-74: LGTM! Filepath handling is safe.

The length check on line 72 ensures the slice has at least one element before accessing index 0 on line 73. In Go, len() safely returns 0 for nil slices, making this guard sufficient.


254-254: LGTM! Await-based RPC call is correctly implemented.

The migration to the Await pattern is correct, with proper context usage and error handling that checks for closed RPC connections.


312-312: LGTM! Shutdown RPC call properly handles context lifecycle.

The Await-based call correctly uses the shutdown context that is properly canceled via the defer on line 295.


411-420: LGTM! Initialization retry logic correctly migrated to Await pattern.

The RPC call properly uses the Await pattern with appropriate error handling and retry logic. The error variable is correctly scoped within the loop, and the break on line 420 ensures the loop exits on successful initialization.

external-providers/generic-external-provider/go.mod (3)

32-37: No compatibility issues found — versions are consistent and correctly resolved.

The transitive dependencies in external-providers/generic-external-provider/go.mod are identical to those in the root go.mod:

  • google.golang.org/grpc v1.73.0 and google.golang.org/protobuf v1.36.6 match the root module's direct requires
  • golang.org/x/net v0.38.0, golang.org/x/sys v0.31.0, and golang.org/x/text v0.23.0 match the root module's transitive dependencies
  • google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 matches the root module's indirect dependency

The external provider correctly marks these as // indirect because they are inherited transitively via the replace github.com/konveyor/analyzer-lsp => ../../ directive. This is the expected behavior and confirms proper dependency resolution.


16-16: go-winio v0.6.2 is verified as stable and secure—no action required.

Version v0.6.2 is published and actively used by projects like OpenTelemetry, and no security advisories or CVEs were found in GitHub Advisory Database or NVD. The dependency aligns with the PR objective of adding Windows named pipe support and is safe to merge.


27-27: No changes required — OpenTelemetry v1.35.0 is backward-compatible.

The v1.35.0 release (published 2025-03-05) is a backward-compatible minor update, with key changes including logging additions, semantic conventions upgrades, and bug fixes. This is the last release supporting Go 1.22; the next release requires Go 1.23—your codebase targets Go 1.23.9, so this is not a concern.

The async RPC migration uses jsonrpc2_v2 directly; OpenTelemetry is an indirect dependency. No breaking changes affect this pattern.

external-providers/java-external-provider/go.mod (1)

6-47: LGTM: Routine dependency updates.

The dependency version bumps are consistent with the broader PR's upgrade trajectory (OpenTelemetry, gRPC, protobuf ecosystem updates). All changes are minor/patch version increases with no breaking changes expected.

external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

477-477: Good addition for diagnostics.

The error log provides useful context for language server startup failures, aiding troubleshooting during retries.

lsp/base_service_client/std_dialer.go (1)

11-49: Well-structured dialer implementation.

The StdDialer correctly implements the jsonrpc2.Dialer interface with clean delegation to underlying stdin/stdout streams. The Close method properly combines errors from both streams.

lsp/base_service_client/base_service_client.go (1)

306-312: Good handler chaining pattern.

The handler delegation approach correctly tries the custom handler first and falls through to the base handler on unhandled methods. This provides extensibility while maintaining default behavior.

provider/grpc/socket/consts.go (1)

1-5: LGTM: Reasonable message size constant.

8MB is a sensible default for gRPC message size limits in socket-based transports. The constant is appropriately scoped and well-named.

provider/internal/grpc/library.proto (1)

23-24: LGTM: Well-defined proto fields for socket support.

The new languageServerPipe and initialized fields support the socket-based serving pattern. Field names are correctly spelled, and field numbers (7, 8) follow sequential ordering without conflicts.

external-providers/dotnet-external-provider/main.go (1)

21-21: LGTM: Consistent socket support pattern.

The socket flag, validation logic, and server initialization follow the same pattern as other external providers in this PR. The validation correctly requires either a port or socket, and error messaging is clear.

Also applies to: 38-40, 59-59

external-providers/java-external-provider/main.go (1)

17-17: LGTM: Socket support properly integrated.

The socket flag and validation logic mirror the pattern established in other providers. The implementation is consistent and correct.

Also applies to: 41-43, 61-61

provider/server.go (1)

253-266: LGTM: RPC client uses background ctx and handler.

Correct lifecycle choice; connection won’t be tied to Init request.

Based on learnings.

provider/grpc/provider.go (1)

194-207: LGTM: config propagation to pb.Config (incl. pipe/init flags).

provider/grpc/socket/pipe_windows.go (1)

26-39: LGTM: use unix:// with passthrough for named pipes (intentional quirk).

Based on learnings.

@pranavgaikwad
Copy link
Contributor

@shawn-hurley I just have a question about Kai, are you looking to make a change in the RPC client passed by the kai_analyzer_rpc, or it can work for now with the old client?

@savitharaghunathan
Copy link
Member

@shawn-hurley the wrapper changes look good to me. do we have to update the documentation anywhere with the support for socket mode? I see the sample config files.

@shawn-hurley
Copy link
Contributor Author

@savitharaghunathan +1 on documentation update (just in general lol). I am going to create a follow-up issue to track that if that is ok?

@pranavgaikwad Depending on how today goes, and how far @djzager is on breaking out the new extension, there should be changes going into kai_analyzer_rpc. What are you looking to do in that?

@djzager
Copy link
Member

djzager commented Oct 24, 2025

@pranavgaikwad Depending on how today goes, and how far @djzager is on breaking out the new extension, there should be changes going into kai_analyzer_rpc. What are you looking to do in that?

Right now I have two extensions split but the java extension doesn't really do anything. I'm trying to see how far I can go without needing java-external-provider being pulled out of kai-analyzer-rpc. Should be ready for POC kai-analyzer-rpc and java-external-provider early next week.

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.

5 participants