@@ -18,6 +18,9 @@ import (
1818 "strings"
1919 "time"
2020
21+ "github.com/onflow/flow-go/module/component"
22+ "github.com/onflow/flow-go/module/irrecoverable"
23+
2124 gethVM "github.com/onflow/go-ethereum/core/vm"
2225 gethLog "github.com/onflow/go-ethereum/log"
2326 "github.com/onflow/go-ethereum/rpc"
@@ -55,8 +58,12 @@ type Server struct {
5558
5659 config config.Config
5760 collector metrics.Collector
61+
62+ startupCompleted chan struct {}
5863}
5964
65+ var _ component.Component = (* Server )(nil )
66+
6067const (
6168 shutdownTimeout = 5 * time .Second
6269 batchRequestLimit = 50
@@ -77,10 +84,11 @@ func NewServer(
7784 gethLog .SetDefault (gethLog .NewLogger (zeroSlog ))
7885
7986 return & Server {
80- logger : logger ,
81- timeouts : rpc .DefaultHTTPTimeouts ,
82- config : cfg ,
83- collector : collector ,
87+ logger : logger ,
88+ timeouts : rpc .DefaultHTTPTimeouts ,
89+ config : cfg ,
90+ collector : collector ,
91+ startupCompleted : make (chan struct {}),
8492 }
8593}
8694
@@ -177,9 +185,10 @@ func (h *Server) disableWS() bool {
177185}
178186
179187// Start starts the HTTP server if it is enabled and not already running.
180- func (h * Server ) Start () error {
188+ func (h * Server ) Start (ctx irrecoverable.SignalerContext ) {
189+ defer close (h .startupCompleted )
181190 if h .endpoint == "" || h .listener != nil {
182- return nil // already running or not configured
191+ return // already running or not configured
183192 }
184193
185194 // Initialize the server.
@@ -190,16 +199,21 @@ func (h *Server) Start() error {
190199 h .server .ReadHeaderTimeout = h .timeouts .ReadHeaderTimeout
191200 h .server .WriteTimeout = h .timeouts .WriteTimeout
192201 h .server .IdleTimeout = h .timeouts .IdleTimeout
202+ h .server .BaseContext = func (_ net.Listener ) context.Context {
203+ return ctx
204+ }
193205 }
194206
207+ listenConfig := net.ListenConfig {}
195208 // Start the server.
196- listener , err := net .Listen ("tcp" , h .endpoint )
209+ listener , err := listenConfig .Listen (ctx , "tcp" , h .endpoint )
197210 if err != nil {
198211 // If the server fails to start, we need to clear out the RPC and WS
199212 // configurations so they can be configured another time.
200213 h .disableRPC ()
201214 h .disableWS ()
202- return err
215+ ctx .Throw (err )
216+ return
203217 }
204218
205219 h .listener = listener
@@ -211,7 +225,7 @@ func (h *Server) Start() error {
211225 return
212226 }
213227 h .logger .Err (err ).Msg ("failed to start API server" )
214- panic (err )
228+ ctx . Throw (err )
215229 }
216230 }()
217231
@@ -223,8 +237,17 @@ func (h *Server) Start() error {
223237 url := fmt .Sprintf ("ws://%v" , listener .Addr ())
224238 h .logger .Info ().Msgf ("JSON-RPC over WebSocket enabled: %s" , url )
225239 }
240+ }
226241
227- return nil
242+ func (h * Server ) Ready () <- chan struct {} {
243+ ready := make (chan struct {})
244+
245+ go func () {
246+ <- h .startupCompleted
247+ close (ready )
248+ }()
249+
250+ return ready
228251}
229252
230253// disableRPC stops the JSON-RPC over HTTP handler.
@@ -294,41 +317,50 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
294317 w .WriteHeader (http .StatusNotFound )
295318}
296319
297- // Stop shuts down the HTTP server.
298- func (h * Server ) Stop () {
299- if h .listener == nil {
300- return // not running
301- }
320+ // Done shuts down the HTTP server.
321+ func (h * Server ) Done () <- chan struct {} {
322+ done := make (chan struct {})
302323
303- // Shut down the server.
304- httpHandler := h .httpHandler
305- if httpHandler != nil {
306- httpHandler .server .Stop ()
307- h .httpHandler = nil
308- }
324+ go func () {
325+ defer close (done )
309326
310- wsHandler := h .wsHandler
311- if wsHandler != nil {
312- wsHandler .server .Stop ()
313- h .wsHandler = nil
314- }
327+ if h .listener == nil {
328+ return // not running
329+ }
315330
316- ctx , cancel := context .WithTimeout (context .Background (), shutdownTimeout )
317- defer cancel ()
318- err := h .server .Shutdown (ctx )
319- if err != nil && err == ctx .Err () {
320- h .logger .Warn ().Msg ("HTTP server graceful shutdown timed out" )
321- h .server .Close ()
322- }
331+ // Shut down the server.
332+ httpHandler := h .httpHandler
333+ if httpHandler != nil {
334+ httpHandler .server .Stop ()
335+ h .httpHandler = nil
336+ }
337+
338+ wsHandler := h .wsHandler
339+ if wsHandler != nil {
340+ wsHandler .server .Stop ()
341+ h .wsHandler = nil
342+ }
343+
344+ ctx , cancel := context .WithTimeout (context .Background (), shutdownTimeout )
345+ defer cancel ()
346+ err := h .server .Shutdown (ctx )
347+ if err != nil && err == ctx .Err () {
348+ h .logger .Warn ().Msg ("HTTP server graceful shutdown timed out" )
349+ h .server .Close ()
350+ }
323351
324- h .listener .Close ()
325- h .logger .Info ().Msgf (
326- "HTTP server stopped, endpoint: %s" , h .listener .Addr (),
327- )
352+ h .listener .Close ()
353+ h .logger .Info ().Msgf (
354+ "HTTP server stopped, endpoint: %s" , h .listener .Addr (),
355+ )
356+
357+ // Clear out everything to allow re-configuring it later.
358+ h .host , h .port , h .endpoint = "" , 0 , ""
359+ h .server , h .listener = nil , nil
360+
361+ }()
328362
329- // Clear out everything to allow re-configuring it later.
330- h .host , h .port , h .endpoint = "" , 0 , ""
331- h .server , h .listener = nil , nil
363+ return done
332364}
333365
334366// CheckTimeouts ensures that timeout values are meaningful
0 commit comments