mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-18 14:39:49 +02:00
fix(proxmox): prevent goroutine leaks by closing idle HTTP connections
Added a function to close idle HTTP connections in the LXCCommand method. This addresses potential goroutine leaks caused by the go-proxmox library's TermWebSocket not closing underlying HTTP/2 connections. The websocket closer is now wrapped to ensure proper cleanup of transport connections when the command execution is finished.
This commit is contained in:
@@ -5,12 +5,22 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoSession = fmt.Errorf("no session found, make sure username and password are set")
|
var ErrNoSession = fmt.Errorf("no session found, make sure username and password are set")
|
||||||
|
|
||||||
|
// closeTransportConnections forces close idle HTTP connections to prevent goroutine leaks.
|
||||||
|
// This is needed because the go-proxmox library's TermWebSocket closer doesn't close
|
||||||
|
// the underlying HTTP/2 connections, leaving goroutines stuck in writeLoop/readLoop.
|
||||||
|
func closeTransportConnections(httpClient *http.Client) {
|
||||||
|
if tr, ok := httpClient.Transport.(*http.Transport); ok {
|
||||||
|
tr.CloseIdleConnections()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LXCCommand connects to the Proxmox VNC websocket and streams command output.
|
// LXCCommand connects to the Proxmox VNC websocket and streams command output.
|
||||||
// It returns an io.ReadCloser that streams the command output.
|
// It returns an io.ReadCloser that streams the command output.
|
||||||
func (n *Node) LXCCommand(ctx context.Context, vmid int, command string) (io.ReadCloser, error) {
|
func (n *Node) LXCCommand(ctx context.Context, vmid int, command string) (io.ReadCloser, error) {
|
||||||
@@ -37,11 +47,19 @@ func (n *Node) LXCCommand(ctx context.Context, vmid int, command string) (io.Rea
|
|||||||
return nil, fmt.Errorf("failed to get term proxy: %w", err)
|
return nil, fmt.Errorf("failed to get term proxy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
send, recv, errs, close, err := node.TermWebSocket(term)
|
send, recv, errs, closeWS, err := node.TermWebSocket(term)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to term websocket: %w", err)
|
return nil, fmt.Errorf("failed to connect to term websocket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap the websocket closer to also close HTTP transport connections.
|
||||||
|
// This prevents goroutine leaks when streaming connections are interrupted.
|
||||||
|
httpClient := n.client.GetHTTPClient()
|
||||||
|
closeFn := func() error {
|
||||||
|
closeTransportConnections(httpClient)
|
||||||
|
return closeWS()
|
||||||
|
}
|
||||||
|
|
||||||
handleSend := func(data []byte) error {
|
handleSend := func(data []byte) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -67,7 +85,7 @@ func (n *Node) LXCCommand(ctx context.Context, vmid int, command string) (io.Rea
|
|||||||
|
|
||||||
// Start a goroutine to read from websocket and write to pipe
|
// Start a goroutine to read from websocket and write to pipe
|
||||||
go func() {
|
go func() {
|
||||||
defer close()
|
defer closeFn()
|
||||||
defer pw.Close()
|
defer pw.Close()
|
||||||
|
|
||||||
seenCommand := false
|
seenCommand := false
|
||||||
|
|||||||
Reference in New Issue
Block a user