diff --git a/proxy/receiver_api.go b/proxy/receiver_api.go index 54a70bf..e988046 100644 --- a/proxy/receiver_api.go +++ b/proxy/receiver_api.go @@ -5,6 +5,7 @@ import ( _ "embed" "errors" "log/slog" + "net/http" "time" "github.com/ethereum/go-ethereum/common" @@ -70,12 +71,34 @@ func (prx *ReceiverProxy) UserJSONRPCHandler(maxRequestBodySizeBytes int64) (*rp Log: prx.Log, MaxRequestBodySizeBytes: maxRequestBodySizeBytes, VerifyRequestSignatureFromHeader: true, + ReadyHandler: prx.readyHandler, }, ) return handler, err } +// readyHandler calls /readyz on rbuilder +func (prx *ReceiverProxy) readyHandler(w http.ResponseWriter, r *http.Request) error { + resp, err := http.Get(prx.localBuilderEndpoint + "/readyz") + if err != nil { + prx.Log.Warn("Failed to check builder readiness", slog.Any("error", err)) + http.Error(w, "not ready", http.StatusServiceUnavailable) + return nil + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + http.Error(w, "not ready", http.StatusServiceUnavailable) + return nil + } + + // If the builder is ready, return 200 OK + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ready")) + return nil +} + func (prx *ReceiverProxy) ValidateSigner(ctx context.Context, req *ParsedRequest, systemEndpoint bool) error { req.signer = rpcserver.GetSigner(ctx) if !systemEndpoint { diff --git a/proxy/receiver_proxy.go b/proxy/receiver_proxy.go index bfdc929..fe7d809 100644 --- a/proxy/receiver_proxy.go +++ b/proxy/receiver_proxy.go @@ -38,7 +38,8 @@ type ReceiverProxy struct { OrderflowSigner *signature.Signer - localBuilder rpcclient.RPCClient + localBuilder rpcclient.RPCClient + localBuilderEndpoint string UserHandler http.Handler SystemHandler http.Handler @@ -108,6 +109,7 @@ func NewReceiverProxy(config ReceiverProxyConfig) (*ReceiverProxy, error) { ConfigHub: NewBuilderConfigHub(config.Log, config.BuilderConfigHubEndpoint), OrderflowSigner: orderflowSigner, localBuilder: localBuilder, + localBuilderEndpoint: config.LocalBuilderEndpoint, requestUniqueKeysRLU: expirable.NewLRU[uuid.UUID, struct{}](requestsRLUSize, nil, requestsRLUTTL), replacementNonceRLU: expirable.NewLRU[replacementNonceKey, int](replacementNonceSize, nil, replacementNonceTTL), userAPIRateLimiter: userAPIRateLimiter, diff --git a/proxy/receiver_proxy_test.go b/proxy/receiver_proxy_test.go index 377a218..3281c4e 100644 --- a/proxy/receiver_proxy_test.go +++ b/proxy/receiver_proxy_test.go @@ -636,3 +636,20 @@ func TestValidateLocalBundles(t *testing.T) { proxiesFlushQueue() } + +func TestJSONRPCServerReadyzOK(t *testing.T) { + tempDir := t.TempDir() + certPath := path.Join(tempDir, "cert") + keyPath := path.Join(tempDir, "key") + proxy, err := StartTestOrderflowProxy("1", certPath, keyPath) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodGet, "/readyz", nil) + + rr := httptest.NewRecorder() + proxy.localServer.Handler.ServeHTTP(rr, req) + respBody, err := io.ReadAll(rr.Body) + require.NoError(t, err) + require.Equal(t, http.StatusOK, rr.Code) + require.Contains(t, string(respBody), "ready") +}