github.com/onsi/ginkgo@v1.16.6-0.20211118180735-4e1925ba4c95/internal/parallel_support/http_server.go (about)

     1  /*
     2  
     3  The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners.
     4  This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser).
     5  
     6  */
     7  
     8  package parallel_support
     9  
    10  import (
    11  	"encoding/json"
    12  	"io"
    13  	"net"
    14  	"net/http"
    15  
    16  	"github.com/onsi/ginkgo/reporters"
    17  	"github.com/onsi/ginkgo/types"
    18  )
    19  
    20  /*
    21  httpServer spins up on an automatically selected port and listens for communication from the forwarding reporter.
    22  It then forwards that communication to attached reporters.
    23  */
    24  type httpServer struct {
    25  	listener net.Listener
    26  	handler  *ServerHandler
    27  }
    28  
    29  //Create a new server, automatically selecting a port
    30  func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) {
    31  	listener, err := net.Listen("tcp", "127.0.0.1:0")
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	return &httpServer{
    36  		listener: listener,
    37  		handler:  newServerHandler(parallelTotal, reporter),
    38  	}, nil
    39  }
    40  
    41  //Start the server.  You don't need to `go s.Start()`, just `s.Start()`
    42  func (server *httpServer) Start() {
    43  	httpServer := &http.Server{}
    44  	mux := http.NewServeMux()
    45  	httpServer.Handler = mux
    46  
    47  	//streaming endpoints
    48  	mux.HandleFunc("/suite-will-begin", server.specSuiteWillBegin)
    49  	mux.HandleFunc("/did-run", server.didRun)
    50  	mux.HandleFunc("/suite-did-end", server.specSuiteDidEnd)
    51  	mux.HandleFunc("/emit-output", server.emitOutput)
    52  
    53  	//synchronization endpoints
    54  	mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted)
    55  	mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState)
    56  	mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished)
    57  	mux.HandleFunc("/aggregated-nonprimary-procs-report", server.handleAggregatedNonprimaryProcsReport)
    58  	mux.HandleFunc("/counter", server.handleCounter)
    59  	mux.HandleFunc("/up", server.handleUp)
    60  	mux.HandleFunc("/abort", server.handleAbort)
    61  
    62  	go httpServer.Serve(server.listener)
    63  }
    64  
    65  //Stop the server
    66  func (server *httpServer) Close() {
    67  	server.listener.Close()
    68  }
    69  
    70  //The address the server can be reached it.  Pass this into the `ForwardingReporter`.
    71  func (server *httpServer) Address() string {
    72  	return "http://" + server.listener.Addr().String()
    73  }
    74  
    75  func (server *httpServer) GetSuiteDone() chan interface{} {
    76  	return server.handler.done
    77  }
    78  
    79  func (server *httpServer) GetOutputDestination() io.Writer {
    80  	return server.handler.outputDestination
    81  }
    82  
    83  func (server *httpServer) SetOutputDestination(w io.Writer) {
    84  	server.handler.outputDestination = w
    85  }
    86  
    87  func (server *httpServer) RegisterAlive(node int, alive func() bool) {
    88  	server.handler.registerAlive(node, alive)
    89  }
    90  
    91  //
    92  // Streaming Endpoints
    93  //
    94  
    95  //The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
    96  func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool {
    97  	defer request.Body.Close()
    98  	if json.NewDecoder(request.Body).Decode(object) != nil {
    99  		writer.WriteHeader(http.StatusBadRequest)
   100  		return false
   101  	}
   102  	return true
   103  }
   104  
   105  func (server *httpServer) handleError(err error, writer http.ResponseWriter) bool {
   106  	if err == nil {
   107  		return false
   108  	}
   109  	switch err {
   110  	case ErrorEarly:
   111  		writer.WriteHeader(http.StatusTooEarly)
   112  	case ErrorGone:
   113  		writer.WriteHeader(http.StatusGone)
   114  	case ErrorFailed:
   115  		writer.WriteHeader(http.StatusFailedDependency)
   116  	default:
   117  		writer.WriteHeader(http.StatusInternalServerError)
   118  	}
   119  	return true
   120  }
   121  
   122  func (server *httpServer) specSuiteWillBegin(writer http.ResponseWriter, request *http.Request) {
   123  	var report types.Report
   124  	if !server.decode(writer, request, &report) {
   125  		return
   126  	}
   127  
   128  	server.handleError(server.handler.SpecSuiteWillBegin(report, voidReceiver), writer)
   129  }
   130  
   131  func (server *httpServer) didRun(writer http.ResponseWriter, request *http.Request) {
   132  	var report types.SpecReport
   133  	if !server.decode(writer, request, &report) {
   134  		return
   135  	}
   136  
   137  	server.handleError(server.handler.DidRun(report, voidReceiver), writer)
   138  }
   139  
   140  func (server *httpServer) specSuiteDidEnd(writer http.ResponseWriter, request *http.Request) {
   141  	var report types.Report
   142  	if !server.decode(writer, request, &report) {
   143  		return
   144  	}
   145  	server.handleError(server.handler.SpecSuiteDidEnd(report, voidReceiver), writer)
   146  }
   147  
   148  func (server *httpServer) emitOutput(writer http.ResponseWriter, request *http.Request) {
   149  	output, err := io.ReadAll(request.Body)
   150  	if err != nil {
   151  		writer.WriteHeader(http.StatusInternalServerError)
   152  		return
   153  	}
   154  	var n int
   155  	server.handleError(server.handler.EmitOutput(output, &n), writer)
   156  }
   157  
   158  func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) {
   159  	var beforeSuiteState BeforeSuiteState
   160  	if !server.decode(writer, request, &beforeSuiteState) {
   161  		return
   162  	}
   163  
   164  	server.handleError(server.handler.BeforeSuiteCompleted(beforeSuiteState, voidReceiver), writer)
   165  }
   166  
   167  func (server *httpServer) handleBeforeSuiteState(writer http.ResponseWriter, request *http.Request) {
   168  	var beforeSuiteState BeforeSuiteState
   169  	if server.handleError(server.handler.BeforeSuiteState(voidSender, &beforeSuiteState), writer) {
   170  		return
   171  	}
   172  	json.NewEncoder(writer).Encode(beforeSuiteState)
   173  }
   174  
   175  func (server *httpServer) handleHaveNonprimaryProcsFinished(writer http.ResponseWriter, request *http.Request) {
   176  	if server.handleError(server.handler.HaveNonprimaryProcsFinished(voidSender, voidReceiver), writer) {
   177  		return
   178  	}
   179  	writer.WriteHeader(http.StatusOK)
   180  }
   181  
   182  func (server *httpServer) handleAggregatedNonprimaryProcsReport(writer http.ResponseWriter, request *http.Request) {
   183  	var aggregatedReport types.Report
   184  	if server.handleError(server.handler.AggregatedNonprimaryProcsReport(voidSender, &aggregatedReport), writer) {
   185  		return
   186  	}
   187  	json.NewEncoder(writer).Encode(aggregatedReport)
   188  }
   189  
   190  func (server *httpServer) handleCounter(writer http.ResponseWriter, request *http.Request) {
   191  	var n int
   192  	if server.handleError(server.handler.Counter(voidSender, &n), writer) {
   193  		return
   194  	}
   195  	json.NewEncoder(writer).Encode(ParallelIndexCounter{Index: n})
   196  }
   197  
   198  func (server *httpServer) handleUp(writer http.ResponseWriter, request *http.Request) {
   199  	writer.WriteHeader(http.StatusOK)
   200  }
   201  
   202  func (server *httpServer) handleAbort(writer http.ResponseWriter, request *http.Request) {
   203  	if request.Method == "GET" {
   204  		var shouldAbort bool
   205  		server.handler.ShouldAbort(voidSender, &shouldAbort)
   206  		if shouldAbort {
   207  			writer.WriteHeader(http.StatusGone)
   208  		} else {
   209  			writer.WriteHeader(http.StatusOK)
   210  		}
   211  	} else {
   212  		server.handler.Abort(voidSender, voidReceiver)
   213  	}
   214  }