diff --git a/cmd/dependabot/internal/cmd/update.go b/cmd/dependabot/internal/cmd/update.go index e5f3d3b..1ecc1ab 100644 --- a/cmd/dependabot/internal/cmd/update.go +++ b/cmd/dependabot/internal/cmd/update.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "log" + "net" "os" "github.com/MakeNowJust/heredoc" @@ -140,7 +141,11 @@ func extractInput(cmd *cobra.Command, flags *UpdateFlags) (*model.Input, error) } if hasServer { - return server.Input(flags.inputServerPort) + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", flags.inputServerPort)) + if err != nil { + return nil, fmt.Errorf("failed to create listener: %w", err) + } + return server.Input(l) } if hasStdin { diff --git a/internal/server/input.go b/internal/server/input.go index 151766e..3e53aac 100644 --- a/internal/server/input.go +++ b/internal/server/input.go @@ -1,19 +1,17 @@ package server import ( - "context" "encoding/json" - "fmt" + "errors" + "github.com/dependabot/cli/internal/model" "log" + "net" "net/http" - "time" - - "github.com/dependabot/cli/internal/model" ) type credServer struct { - server *http.Server - data *model.Input + listener net.Listener + data *model.Input } // the server receives one payload and shuts itself down @@ -24,22 +22,19 @@ func (s *credServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) _ = r.Body.Close() go func() { - _ = s.server.Shutdown(context.Background()) + _ = s.listener.Close() }() } // Input receives configuration via HTTP on the port and returns it decoded -func Input(port int) (*model.Input, error) { - server := &http.Server{ - Addr: fmt.Sprintf("127.0.0.1:%d", port), - ReadHeaderTimeout: time.Second, +func Input(listener net.Listener) (*model.Input, error) { + handler := &credServer{ + listener: listener, } - s := &credServer{server: server} - server.Handler = s // printing so the user doesn't think the cli is hanging - log.Println("waiting for input on port", port) - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Println("waiting for input on", listener.Addr()) + if err := http.Serve(listener, handler); err != nil && !errors.Is(err, net.ErrClosed) { return nil, err } - return s.data, nil + return handler.data, nil } diff --git a/internal/server/input_test.go b/internal/server/input_test.go index c5981a3..6033889 100644 --- a/internal/server/input_test.go +++ b/internal/server/input_test.go @@ -2,33 +2,43 @@ package server import ( "bytes" + "fmt" + "github.com/dependabot/cli/internal/model" + "net" "net/http" "sync" "testing" - "time" - - "github.com/dependabot/cli/internal/model" ) func TestInput(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) var input *model.Input + + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("Failed to create listener: ", err.Error()) + } + go func() { - input, _ = Input(8080) + input, err = Input(l) + if err != nil { + t.Errorf(err.Error()) + } wg.Done() }() - // give the server time to start - time.Sleep(10 * time.Millisecond) + url := fmt.Sprintf("http://%s", l.Addr().String()) data := `{"job":{"package-manager":"test"},"credentials":[{"credential":"value"}]}` - resp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader([]byte(data))) + resp, err := http.Post(url, "application/json", bytes.NewReader([]byte(data))) if err != nil { t.Fatal(err.Error()) } if resp.StatusCode != 200 { t.Errorf("expected status code 200, got %d", resp.StatusCode) } + + // Test will hang here if the server does not shut down wg.Wait() if input.Job.PackageManager != "test" {