Go Http Handler is restarting itself during the execution of a command (cmd.Command)

Solution 1:

There's a couple of things of note:

  • capturing stdout and stderr seems brittle (error handling from goroutine will not propagate properly)
  • long running executable may cause request timeout issues

to capture stdout and stderr - I'd suggest using something like exec.Cmd.CombinedOutput e.g.

cmd := exec.CommandContext(ctx, "flutter", "build", "appbundle") // see below for ctx details

var sout, serr bytes.Buffer
c.Stdout = &sout
c.Stderr = &serr
err := c.Run()
if err != nil { /* ... */ }

outBody, errBody := string(sout.Bytes()), string(serr.Bytes())

so no need for tricky wait-groups or complex error handling between goroutines.


You need to handle the case where web-requests may take too long. If a request is canceled (by the client or the server) - you need a mechanism in place to cancel the request's sub-tasks (e.g. you externally executed process)

When running any webservice always ensure to leverage context.Context for the lifetime of a request - especially when running any potentially blocking operations (as you are with exec.Command).

So first, from your handler, grab the request's context.Context, and pass this along to any potentially blocking calls:

func (h *HttpServer) GenerateMobileApp(w http.ResponseWriter, r *http.Request) {
    ...
    ctx := r.Context()  // context.Context for the lifetime of the request

    ...
    go func() {
        apkGeneratedPath, err := h.mobileAPP.GenerateAPK(ctx, project.Id) // <- add ctx
    }

}

and update your APK generator signature like so:

func (app *MobileAPP) GenerateAPK(ctx context.Context, projectID string) (string, error)

and to use ctx when executing an external tool:

//cmd := exec.Command("flutter", "build", "appbundle")
cmd := exec.CommandContext(ctx, "flutter", "build", "appbundle")

with this in place, if the web request is canceled (times out - based on web server settings; or the client disconnects) the process will be terminated. You can even have special clean-up logic in place for this occurrence, by checking the error from exec.CommandContext for either context.Canceled (client disconnected) or context.DeadlineExceeded (request timed-out).