Skip to content

CI: test and fix portfwd issue (w3m -dump hangs) #3708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ jobs:
# QEMU: required by Lima itself
# bash: required by test-templates.sh (OS version of bash is too old)
# coreutils: required by test-templates.sh for the "timeout" command
run: brew install qemu bash coreutils
# w3m : required by test-templates.sh for port forwarding tests
run: brew install qemu bash coreutils w3m
- name: "Adjust LIMACTL_CREATE_ARGS"
run: echo "LIMACTL_CREATE_ARGS=${LIMACTL_CREATE_ARGS} --vm-type=qemu" >>$GITHUB_ENV
- name: "Inject `no_timer_check` to kernel cmdline"
Expand Down Expand Up @@ -331,7 +332,7 @@ jobs:
- name: Install test dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends ovmf qemu-system-x86 qemu-utils
sudo apt-get install -y --no-install-recommends ovmf qemu-system-x86 qemu-utils w3m
sudo modprobe kvm
# `sudo usermod -aG kvm $(whoami)` does not take an effect on GHA
sudo chown $(whoami) /dev/kvm
Expand Down Expand Up @@ -430,7 +431,7 @@ jobs:
with:
template: templates/default.yaml
- name: Install test dependencies
run: brew install qemu bash coreutils
run: brew install qemu bash coreutils w3m
- name: Install socket_vmnet
env:
SOCKET_VMNET_VERSION: v1.2.0
Expand Down Expand Up @@ -525,7 +526,7 @@ jobs:
with:
template: templates/${{ matrix.template }}
- name: Install test dependencies
run: brew install bash coreutils
run: brew install bash coreutils w3m
- name: Uninstall qemu
run: brew uninstall --ignore-dependencies --force qemu
- name: Test
Expand Down
9 changes: 9 additions & 0 deletions hack/test-templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,15 @@ if [[ -n ${CHECKS["port-forwards"]} ]]; then
limactl shell "$NAME" $sudo $CONTAINER_ENGINE rm -f nginx
fi
fi
if [[ ${NAME} != "alpine"* && ${NAME} != "wsl2"* ]] && command -v w3m >/dev/null; then
INFO "Testing https://github.com/lima-vm/lima/issues/3685 ([gRPC portfwd] client connection is not closed immediately when server closed the connection)"
# Skip the test on Alpine, as systemd-run is missing
# Skip the test on WSL2, as port forwarding is half broken https://github.com/lima-vm/lima/pull/3686#issuecomment-3034842616
Copy link
Member

Choose a reason for hiding this comment

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

Do we know if the server close the socket or only do shutdown(SHUT_RD)?

Copy link
Member Author

Choose a reason for hiding this comment

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

[pid  2675] write(2, "127.0.0.1 - - [10/Jul/2025 13:43"..., 60127.0.0.1 - - [10/Jul/2025 13:43:44] "GET / HTTP/1.0" 200 -                  
) = 60                                                                                                                                     
[pid  2675] sendto(4, "HTTP/1.0 200 OK\r\nServer: SimpleH"..., 156, 0, NULL, 0) = 156                                                      
[pid  2675] sendto(4, "<!DOCTYPE HTML>\n<html lang=\"en\">"..., 1383, 0, NULL, 0) = 1383                                                   
[pid  2675] shutdown(4, SHUT_WR)        = 0                                                                                                
[pid  2675] close(4)                    = 0 

limactl shell "$NAME" systemd-run --user python3 -m http.server 3685
# curl is not enough to reproduce https://github.com/lima-vm/lima/issues/3685
# `w3m -dump` exits with status code 0 even on "Can't load" error.
timeout 30s bash -euxc "until w3m -dump http://localhost:3685 | grep -v \"w3m: Can't load\"; do sleep 3; done"
fi
fi
set +x
fi
Expand Down
33 changes: 32 additions & 1 deletion pkg/portfwdserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
package portfwdserver

import (
"context"
"errors"
"io"
"net"
"os"
"strings"
"time"

"github.com/containers/gvisor-tap-vsock/pkg/tcpproxy"

"github.com/lima-vm/lima/pkg/bicopy"
"github.com/lima-vm/lima/pkg/guestagent/api"
)
Expand All @@ -35,7 +40,23 @@ func (s *TunnelServer) Start(stream api.GuestService_TunnelServer) error {
return err
}
rw := &GRPCServerRW{stream: stream, id: in.Id}
bicopy.Bicopy(rw, conn, nil)

// FIXME: consolidate bicopy and tcpproxy into one
//
// The bicopy package does not seem to work with `w3m -dump`:
// https://github.com/lima-vm/lima/issues/3685
//
// However, the tcpproxy package can't pass the CI for WSL2 (experimental):
// https://github.com/lima-vm/lima/pull/3686#issuecomment-3034842616
if wsl2, _ := seemsWSL2(); wsl2 {
bicopy.Bicopy(rw, conn, nil)
} else {
proxy := tcpproxy.DialProxy{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return conn, nil
}}
proxy.HandleConn(rw)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This workaround is quite dirty, but let's merge this and call it a day to unblock the v1.2 release

Copy link
Member

Choose a reason for hiding this comment

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

Is there an issue for cleaning this up in the future?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will open an issue after merging this PR

Copy link
Member Author

Choose a reason for hiding this comment

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


return nil
}

Expand Down Expand Up @@ -83,3 +104,13 @@ func (g *GRPCServerRW) SetReadDeadline(_ time.Time) error {
func (g *GRPCServerRW) SetWriteDeadline(_ time.Time) error {
return nil
}

// seemsWSL2 returns whether lima.env contains LIMA_CIDATA_VMTYPE=wsl2 .
// This is a temporary workaround and has to be removed.
func seemsWSL2() (bool, error) {
b, err := os.ReadFile("/mnt/lima-cidata/lima.env")
if err != nil {
return false, err
}
return strings.Contains(string(b), "LIMA_CIDATA_VMTYPE=wsl2"), nil
}
Loading