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 }