@@ -236,10 +236,6 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
236236
237237 log .Printf ("[%s] incoming HTTP request from %s\n " , rid , r .RemoteAddr )
238238
239- for _ , responseHeader := range responseHeaders {
240- w .Header ().Set (responseHeader .Name , responseHeader .Value )
241- }
242-
243239 id := mux .Vars (r )["id" ]
244240
245241 matchedHook := matchLoadedHook (id )
@@ -345,6 +341,7 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
345341 stdoutRdr , stderrRdr , errCh := handleHook (ctx , matchedHook , rid , & headers , & query , & payload , & body )
346342
347343 if matchedHook .StreamCommandStdout {
344+ log .Printf ("[%s] Hook (%s) is a streaming command hook\n " , rid , matchedHook .ID )
348345 // Collect stderr to avoid blocking processes and emit it as a string
349346 stderrRdy := make (chan string , 1 )
350347 go func () {
@@ -362,17 +359,18 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
362359
363360 // Streaming output should commence as soon as the command execution tries to write any data
364361 firstByte := make ([]byte ,1 )
365- _ , err := stdoutRdr .Read (firstByte )
366- if err != nil {
367- w .WriteHeader (http .StatusInternalServerError )
362+ _ , fbErr := stdoutRdr .Read (firstByte )
363+ if fbErr != nil && fbErr != io .EOF {
368364 w .Header ().Set ("Content-Type" , "text/plain; charset=utf-8" )
365+ w .WriteHeader (http .StatusInternalServerError )
369366 fmt .Fprintf (w , "Error occurred while trying to read from the process's first byte. Please check your logs for more details." )
370- }
371-
372- // Did the process throw an error before we read this byte?
373- select {
374- case err := <- errCh :
375- if err != nil {
367+ log .Printf ("[%s] Hook error while reading first byte: %v\n " , rid , err )
368+ return
369+ } else if fbErr == io .EOF {
370+ log .Printf ("[%s] EOF from hook stdout while reading first byte. Waiting for program exit status\n " , rid )
371+ if err := <- errCh ; err != nil {
372+ log .Printf ("[%s] Hook (%s) returned an error before the first byte. Collecting stderr and failing.\n " , rid , matchedHook .ID )
373+ w .Header ().Set ("Content-Type" , "text/plain; charset=utf-8" )
376374 w .WriteHeader (http .StatusInternalServerError )
377375 if matchedHook .StreamCommandStderrOnError {
378376 // Wait for the stderr buffer to finish collecting
@@ -382,24 +380,22 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
382380 return
383381 }
384382 } else {
385- w .Header ().Set ("Content-Type" , "text/plain; charset=utf-8" )
386383 fmt .Fprintf (w , "Error occurred while executing the hooks command. Please check your logs for more details." )
387384 }
388385 return // Cannot proceed beyond here
389386 }
390- default :
391- // no error - continue
387+ // early EOF, but program exited successfully so stream as normal.
392388 }
393389
394- // Got the first byte (or possibly nothing) successfully. Write the success header, then commence
395- // streaming.
396- w .WriteHeader (http .StatusOK )
397-
398390 // Write user success headers
399391 for _ , responseHeader := range matchedHook .ResponseHeaders {
400392 w .Header ().Set (responseHeader .Name , responseHeader .Value )
401393 }
402394
395+ // Got the first byte (or possibly nothing) successfully. Write the success header, then commence
396+ // streaming.
397+ w .WriteHeader (http .StatusOK )
398+
403399 if _ , err := w .Write (firstByte ); err != nil {
404400 // Hard fail, client has disconnected or otherwise we can't continue.
405401 msg := fmt .Sprintf ("[%s] error while trying to stream first byte: %s" , rid , err )
@@ -418,6 +414,7 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
418414 log .Printf (msg )
419415
420416 } else {
417+ log .Printf ("[%s] Hook (%s) is a conventional command hook\n " , rid , matchedHook .ID )
421418 // Don't break the original API and just combine the streams (specifically, kick off two readers which
422419 // break on newlines and the emit that data in temporal order to the output buffer.
423420 out := combinedOutput (stdoutRdr , stderrRdr )
@@ -426,13 +423,15 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
426423
427424 err := <- errCh
428425
426+ log .Printf ("[%s] got command execution result: %v" , rid , err )
427+
429428 if err != nil {
430429 w .WriteHeader (http .StatusInternalServerError )
431430 } else {
432- w .WriteHeader (http .StatusOK )
433431 for _ , responseHeader := range matchedHook .ResponseHeaders {
434432 w .Header ().Set (responseHeader .Name , responseHeader .Value )
435433 }
434+ w .WriteHeader (http .StatusOK )
436435 }
437436
438437 if matchedHook .CaptureCommandOutput {
@@ -584,9 +583,10 @@ body *[]byte) (io.Reader, io.Reader, <-chan error) {
584583
585584 // Spawn a goroutine to wait for the command to end supply errors
586585 go func () {
587- err := cmd .Wait ()
588- if err != nil {
589- log .Printf ("[%s] error occurred: %+v\n " , rid , err )
586+ resultErr := cmd .Wait ()
587+ close (doneCh ) // Close the doneCh immediately so handlers exit correctly.
588+ if resultErr != nil {
589+ log .Printf ("[%s] error occurred: %+v\n " , rid , resultErr )
590590 }
591591
592592 for i := range files {
@@ -601,9 +601,8 @@ body *[]byte) (io.Reader, io.Reader, <-chan error) {
601601
602602 log .Printf ("[%s] finished handling %s\n " , rid , h .ID )
603603
604- errCh <- err
604+ errCh <- resultErr
605605 close (errCh )
606- close (doneCh )
607606 }()
608607
609608 // Spawn a goroutine which checks if the context is ever cancelled, and sends SIGTERM / SIGKILL if it is
@@ -612,6 +611,7 @@ body *[]byte) (io.Reader, io.Reader, <-chan error) {
612611
613612 select {
614613 case <- ctxDone :
614+ log .Printf ("[%s] Context done (request finished) - killing process." , rid )
615615 // AFAIK this works on Win/Mac/Unix - where does it not work?
616616 if err := cmd .Process .Signal (syscall .SIGTERM ); err != nil {
617617 log .Printf ("[%s] error sending SIGTERM to process for %s: %s\n " , rid , h .ID , err )
0 commit comments