fix: address protocol probe review findings
This commit is contained in:
+9
-86
@@ -2,9 +2,7 @@ package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
@@ -47,14 +45,12 @@ type Model struct {
|
||||
detect func(context.Context, string) (probe.Capabilities, error)
|
||||
}
|
||||
|
||||
// probeDone carries one finished Protocol Probe back to the form. Endpoint and
|
||||
// input text deliberately stay in formState: an asynchronous result cannot
|
||||
// introduce userinfo into status, and formID plus generation reject stale work.
|
||||
// probeDone carries only detector output and formState's opaque typed token.
|
||||
// Source data and all validation/application rules remain owned by formState.
|
||||
type probeDone struct {
|
||||
caps probe.Capabilities
|
||||
err error
|
||||
formID uint64
|
||||
generation uint64
|
||||
caps probe.Capabilities
|
||||
err error
|
||||
token probeToken
|
||||
}
|
||||
|
||||
type profileDeleteConfirmation struct {
|
||||
@@ -177,57 +173,7 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, waitForOperation(progress.updates)
|
||||
}
|
||||
if done, ok := message.(probeDone); ok {
|
||||
if m.page != formPage || !m.form.probe.active || done.formID != m.form.id || done.generation != m.form.probe.generation {
|
||||
return m, nil
|
||||
}
|
||||
m.form.probe.active = false
|
||||
address := m.form.probe.address
|
||||
switch {
|
||||
case done.err != nil:
|
||||
// Detector errors are intentionally not rendered. Production errors are
|
||||
// based on the credential-free dial endpoint, but this boundary also
|
||||
// protects frames if a lower layer returns hostile credential text.
|
||||
m.form.probe.feedback = "Protocol Probe failed for " + address
|
||||
case !done.caps.HTTP && !done.caps.SOCKS5:
|
||||
m.form.probe.feedback = "Protocol Probe: unrecognized protocol at " + address
|
||||
default:
|
||||
raw := m.form.profile.SingleURL
|
||||
if done.caps.HTTP && done.caps.SOCKS5 {
|
||||
httpURL, err := probe.URL(raw, probe.Capabilities{HTTP: true})
|
||||
if err != nil {
|
||||
m.form.probe.feedback = "Protocol Probe could not apply the detected capabilities"
|
||||
return m, nil
|
||||
}
|
||||
socksURL, err := probe.URL(raw, probe.Capabilities{SOCKS5: true})
|
||||
if err != nil {
|
||||
m.form.probe.feedback = "Protocol Probe could not apply the detected capabilities"
|
||||
return m, nil
|
||||
}
|
||||
m.form.profile.Mode = profile.AdvancedMode
|
||||
m.form.profile.HTTPURL = httpURL
|
||||
m.form.profile.HTTPSURL = httpURL
|
||||
m.form.profile.AllURL = strings.Replace(socksURL, "socks5h://", "socks5://", 1)
|
||||
m.form.advancedInitialized = true
|
||||
m.form.probe.field = profile.HTTPURLField
|
||||
m.form.probe.feedback = "Protocol Probe: detected HTTP and SOCKS5 proxy at " + address
|
||||
m.form.rebuildInputs(1)
|
||||
return m, nil
|
||||
}
|
||||
rewritten, err := probe.URL(raw, done.caps)
|
||||
if err != nil {
|
||||
m.form.probe.feedback = "Protocol Probe could not apply the detected capabilities"
|
||||
return m, nil
|
||||
}
|
||||
m.form.profile.SingleURL = rewritten
|
||||
m.form.probe.field = profile.SingleURLField
|
||||
kind := "SOCKS5"
|
||||
if done.caps.HTTP {
|
||||
kind = "HTTP"
|
||||
}
|
||||
m.form.probe.feedback = "Protocol Probe: detected " + kind + " proxy at " + address
|
||||
m.form.rebuildInputs(1)
|
||||
}
|
||||
return m, nil
|
||||
return m, m.form.applyProbeResult(done.token, done.caps, done.err)
|
||||
}
|
||||
key, ok := message.(tea.KeyMsg)
|
||||
if !ok {
|
||||
@@ -596,26 +542,14 @@ func (m Model) updateForm(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
m.form = formState{}
|
||||
return m, nil
|
||||
case tea.KeyCtrlD:
|
||||
if m.form.profile.Mode != profile.SingleMode || m.form.currentField() != profile.SingleURLField || m.form.probe.active {
|
||||
address, token, started := m.form.startProbe()
|
||||
if !started {
|
||||
return m, nil
|
||||
}
|
||||
m.form.syncFocusedValue()
|
||||
m.form.invalidateProbe()
|
||||
m.form.probe.field = profile.SingleURLField
|
||||
address, err := probeAddress(m.form.profile.SingleURL)
|
||||
if err != nil {
|
||||
m.form.probe.feedback = "Protocol Probe: " + err.Error()
|
||||
return m, nil
|
||||
}
|
||||
m.form.probe.active = true
|
||||
m.form.probe.address = address
|
||||
m.form.probe.feedback = "Protocol Probe: checking " + address + "…"
|
||||
detect := m.detect
|
||||
formID := m.form.id
|
||||
generation := m.form.probe.generation
|
||||
return m, func() tea.Msg {
|
||||
caps, err := detect(context.Background(), address)
|
||||
return probeDone{caps: caps, err: err, formID: formID, generation: generation}
|
||||
return probeDone{caps: caps, err: err, token: token}
|
||||
}
|
||||
case tea.KeyCtrlT:
|
||||
return m, m.form.toggleMode()
|
||||
@@ -653,17 +587,6 @@ func (m *Model) applyProfileMutation(intent app.Intent, preferredName string) ap
|
||||
return result
|
||||
}
|
||||
|
||||
// probeAddress extracts a bare host:port from the typed endpoint for dialing
|
||||
// and status text, dropping any userinfo so credentials are never displayed
|
||||
// or sent anywhere.
|
||||
func probeAddress(raw string) (string, error) {
|
||||
u, err := profile.ParseProxyURL(raw)
|
||||
if err != nil {
|
||||
return "", errors.New("Single proxy URL must include host:port")
|
||||
}
|
||||
return u.Host, nil
|
||||
}
|
||||
|
||||
func (m Model) selectedProfile() (profile.Profile, bool) {
|
||||
if m.selectedProfileName != "" {
|
||||
for i := range m.snapshot.Profiles {
|
||||
|
||||
Reference in New Issue
Block a user