From 1b40f941cf08550a29646670b370c3c1d322c2d9 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:38:21 -0500 Subject: [PATCH] middleware for content type and accept headers (#14075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * middleware for content type * adding accept middleware too and tests * Update beacon-chain/rpc/endpoints.go Co-authored-by: Radosław Kapka * Update beacon-chain/rpc/endpoints.go Co-authored-by: Radosław Kapka * Update beacon-chain/rpc/endpoints.go Co-authored-by: Radosław Kapka * Update beacon-chain/rpc/endpoints.go Co-authored-by: Radosław Kapka * including radek's review --------- Co-authored-by: Radosław Kapka --- api/gateway/BUILD.bazel | 2 +- api/gateway/gateway.go | 4 +- api/server/BUILD.bazel | 21 +- api/server/middleware.go | 32 -- api/server/middleware/BUILD.bazel | 29 ++ api/server/middleware/middleware.go | 112 ++++ api/server/middleware/middleware_test.go | 199 +++++++ api/server/{ => middleware}/util.go | 2 +- api/server/{ => middleware}/util_test.go | 2 +- api/server/middleware_test.go | 54 -- beacon-chain/node/BUILD.bazel | 2 +- beacon-chain/node/node.go | 6 +- beacon-chain/rpc/BUILD.bazel | 2 + beacon-chain/rpc/endpoints.go | 636 +++++++++++++++++------ beacon-chain/rpc/service.go | 9 +- validator/node/BUILD.bazel | 2 +- validator/node/node.go | 6 +- 17 files changed, 825 insertions(+), 295 deletions(-) delete mode 100644 api/server/middleware.go create mode 100644 api/server/middleware/BUILD.bazel create mode 100644 api/server/middleware/middleware.go create mode 100644 api/server/middleware/middleware_test.go rename api/server/{ => middleware}/util.go (95%) rename api/server/{ => middleware}/util_test.go (96%) delete mode 100644 api/server/middleware_test.go diff --git a/api/gateway/BUILD.bazel b/api/gateway/BUILD.bazel index 282e7724da..0aafeb7329 100644 --- a/api/gateway/BUILD.bazel +++ b/api/gateway/BUILD.bazel @@ -14,7 +14,7 @@ go_library( "//validator:__subpackages__", ], deps = [ - "//api/server:go_default_library", + "//api/server/middleware:go_default_library", "//runtime:go_default_library", "@com_github_gorilla_mux//:go_default_library", "@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library", diff --git a/api/gateway/gateway.go b/api/gateway/gateway.go index a78500c5a0..29d6365902 100644 --- a/api/gateway/gateway.go +++ b/api/gateway/gateway.go @@ -11,7 +11,7 @@ import ( "github.com/gorilla/mux" gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v5/api/server" + "github.com/prysmaticlabs/prysm/v5/api/server/middleware" "github.com/prysmaticlabs/prysm/v5/runtime" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" @@ -104,7 +104,7 @@ func (g *Gateway) Start() { } } - corsMux := server.CorsHandler(g.cfg.allowedOrigins).Middleware(g.cfg.router) + corsMux := middleware.CorsHandler(g.cfg.allowedOrigins).Middleware(g.cfg.router) if g.cfg.muxHandler != nil { g.cfg.router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/api/server/BUILD.bazel b/api/server/BUILD.bazel index d6d0ef1182..ad642cf9e9 100644 --- a/api/server/BUILD.bazel +++ b/api/server/BUILD.bazel @@ -2,29 +2,14 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "error.go", - "middleware.go", - "util.go", - ], + srcs = ["error.go"], importpath = "github.com/prysmaticlabs/prysm/v5/api/server", visibility = ["//visibility:public"], - deps = [ - "@com_github_gorilla_mux//:go_default_library", - "@com_github_rs_cors//:go_default_library", - ], ) go_test( name = "go_default_test", - srcs = [ - "error_test.go", - "middleware_test.go", - "util_test.go", - ], + srcs = ["error_test.go"], embed = [":go_default_library"], - deps = [ - "//testing/assert:go_default_library", - "//testing/require:go_default_library", - ], + deps = ["//testing/assert:go_default_library"], ) diff --git a/api/server/middleware.go b/api/server/middleware.go deleted file mode 100644 index 13afd71fbc..0000000000 --- a/api/server/middleware.go +++ /dev/null @@ -1,32 +0,0 @@ -package server - -import ( - "net/http" - - "github.com/gorilla/mux" - "github.com/rs/cors" -) - -// NormalizeQueryValuesHandler normalizes an input query of "key=value1,value2,value3" to "key=value1&key=value2&key=value3" -func NormalizeQueryValuesHandler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - NormalizeQueryValues(query) - r.URL.RawQuery = query.Encode() - - next.ServeHTTP(w, r) - }) -} - -// CorsHandler sets the cors settings on api endpoints -func CorsHandler(allowOrigins []string) mux.MiddlewareFunc { - c := cors.New(cors.Options{ - AllowedOrigins: allowOrigins, - AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodOptions}, - AllowCredentials: true, - MaxAge: 600, - AllowedHeaders: []string{"*"}, - }) - - return c.Handler -} diff --git a/api/server/middleware/BUILD.bazel b/api/server/middleware/BUILD.bazel new file mode 100644 index 0000000000..c210e5049d --- /dev/null +++ b/api/server/middleware/BUILD.bazel @@ -0,0 +1,29 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "middleware.go", + "util.go", + ], + importpath = "github.com/prysmaticlabs/prysm/v5/api/server/middleware", + visibility = ["//visibility:public"], + deps = [ + "@com_github_gorilla_mux//:go_default_library", + "@com_github_rs_cors//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "middleware_test.go", + "util_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//api:go_default_library", + "//testing/assert:go_default_library", + "//testing/require:go_default_library", + ], +) diff --git a/api/server/middleware/middleware.go b/api/server/middleware/middleware.go new file mode 100644 index 0000000000..d935fa265f --- /dev/null +++ b/api/server/middleware/middleware.go @@ -0,0 +1,112 @@ +package middleware + +import ( + "fmt" + "net/http" + "strings" + + "github.com/gorilla/mux" + "github.com/rs/cors" +) + +// NormalizeQueryValuesHandler normalizes an input query of "key=value1,value2,value3" to "key=value1&key=value2&key=value3" +func NormalizeQueryValuesHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + NormalizeQueryValues(query) + r.URL.RawQuery = query.Encode() + + next.ServeHTTP(w, r) + }) +} + +// CorsHandler sets the cors settings on api endpoints +func CorsHandler(allowOrigins []string) mux.MiddlewareFunc { + c := cors.New(cors.Options{ + AllowedOrigins: allowOrigins, + AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodOptions}, + AllowCredentials: true, + MaxAge: 600, + AllowedHeaders: []string{"*"}, + }) + + return c.Handler +} + +// ContentTypeHandler checks request for the appropriate media types otherwise returning a http.StatusUnsupportedMediaType error +func ContentTypeHandler(acceptedMediaTypes []string) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // skip the GET request + if r.Method == http.MethodGet { + next.ServeHTTP(w, r) + return + } + contentType := r.Header.Get("Content-Type") + if contentType == "" { + http.Error(w, "Content-Type header is missing", http.StatusUnsupportedMediaType) + return + } + + accepted := false + for _, acceptedType := range acceptedMediaTypes { + if strings.TrimSpace(contentType) == strings.TrimSpace(acceptedType) { + accepted = true + break + } + } + + if !accepted { + http.Error(w, fmt.Sprintf("Unsupported media type: %s", contentType), http.StatusUnsupportedMediaType) + return + } + + next.ServeHTTP(w, r) + }) + } +} + +// AcceptHeaderHandler checks if the client's response preference is handled +func AcceptHeaderHandler(serverAcceptedTypes []string) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + acceptHeader := r.Header.Get("Accept") + // header is optional and should skip if not provided + if acceptHeader == "" { + next.ServeHTTP(w, r) + return + } + + accepted := false + acceptTypes := strings.Split(acceptHeader, ",") + // follows rules defined in https://datatracker.ietf.org/doc/html/rfc2616#section-14.1 + for _, acceptType := range acceptTypes { + acceptType = strings.TrimSpace(acceptType) + if acceptType == "*/*" { + accepted = true + break + } + for _, serverAcceptedType := range serverAcceptedTypes { + if strings.HasPrefix(acceptType, serverAcceptedType) { + accepted = true + break + } + if acceptType != "/*" && strings.HasSuffix(acceptType, "/*") && strings.HasPrefix(serverAcceptedType, acceptType[:len(acceptType)-2]) { + accepted = true + break + } + } + if accepted { + break + } + } + + if !accepted { + http.Error(w, fmt.Sprintf("Not Acceptable: %s", acceptHeader), http.StatusNotAcceptable) + return + } + + next.ServeHTTP(w, r) + }) + } +} diff --git a/api/server/middleware/middleware_test.go b/api/server/middleware/middleware_test.go new file mode 100644 index 0000000000..5869de26b0 --- /dev/null +++ b/api/server/middleware/middleware_test.go @@ -0,0 +1,199 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/prysmaticlabs/prysm/v5/api" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +func TestNormalizeQueryValuesHandler(t *testing.T) { + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("next handler")) + require.NoError(t, err) + }) + + handler := NormalizeQueryValuesHandler(nextHandler) + + tests := []struct { + name string + inputQuery string + expectedQuery string + }{ + { + name: "3 values", + inputQuery: "key=value1,value2,value3", + expectedQuery: "key=value1&key=value2&key=value3", // replace with expected normalized value + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req, err := http.NewRequest("GET", "/test?"+test.inputQuery, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK) + } + + if req.URL.RawQuery != test.expectedQuery { + t.Errorf("query not normalized: got %v want %v", req.URL.RawQuery, test.expectedQuery) + } + + if rr.Body.String() != "next handler" { + t.Errorf("next handler was not executed") + } + }) + } +} + +func TestContentTypeHandler(t *testing.T) { + acceptedMediaTypes := []string{api.JsonMediaType, api.OctetStreamMediaType} + + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("next handler")) + require.NoError(t, err) + }) + + handler := ContentTypeHandler(acceptedMediaTypes)(nextHandler) + + tests := []struct { + name string + contentType string + expectedStatusCode int + isGet bool + }{ + { + name: "Accepted Content-Type - application/json", + contentType: api.JsonMediaType, + expectedStatusCode: http.StatusOK, + }, + { + name: "Accepted Content-Type - ssz format", + contentType: api.OctetStreamMediaType, + expectedStatusCode: http.StatusOK, + }, + { + name: "Unsupported Content-Type - text/plain", + contentType: "text/plain", + expectedStatusCode: http.StatusUnsupportedMediaType, + }, + { + name: "Missing Content-Type", + contentType: "", + expectedStatusCode: http.StatusUnsupportedMediaType, + }, + { + name: "GET request skips content type check", + contentType: "", + expectedStatusCode: http.StatusOK, + isGet: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + httpMethod := http.MethodPost + if tt.isGet { + httpMethod = http.MethodGet + } + req := httptest.NewRequest(httpMethod, "/", nil) + if tt.contentType != "" { + req.Header.Set("Content-Type", tt.contentType) + } + rr := httptest.NewRecorder() + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != tt.expectedStatusCode { + t.Errorf("handler returned wrong status code: got %v want %v", status, tt.expectedStatusCode) + } + }) + } +} + +func TestAcceptHeaderHandler(t *testing.T) { + acceptedTypes := []string{"application/json", "application/octet-stream"} + + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("next handler")) + require.NoError(t, err) + }) + + handler := AcceptHeaderHandler(acceptedTypes)(nextHandler) + + tests := []struct { + name string + acceptHeader string + expectedStatusCode int + }{ + { + name: "Accepted Accept-Type - application/json", + acceptHeader: "application/json", + expectedStatusCode: http.StatusOK, + }, + { + name: "Accepted Accept-Type - application/octet-stream", + acceptHeader: "application/octet-stream", + expectedStatusCode: http.StatusOK, + }, + { + name: "Accepted Accept-Type with parameters", + acceptHeader: "application/json;q=0.9, application/octet-stream;q=0.8", + expectedStatusCode: http.StatusOK, + }, + { + name: "Unsupported Accept-Type - text/plain", + acceptHeader: "text/plain", + expectedStatusCode: http.StatusNotAcceptable, + }, + { + name: "Missing Accept header", + acceptHeader: "", + expectedStatusCode: http.StatusOK, + }, + { + name: "*/* is accepted", + acceptHeader: "*/*", + expectedStatusCode: http.StatusOK, + }, + { + name: "application/* is accepted", + acceptHeader: "application/*", + expectedStatusCode: http.StatusOK, + }, + { + name: "/* is unsupported", + acceptHeader: "/*", + expectedStatusCode: http.StatusNotAcceptable, + }, + { + name: "application/ is unsupported", + acceptHeader: "application/", + expectedStatusCode: http.StatusNotAcceptable, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest("GET", "/", nil) + if tt.acceptHeader != "" { + req.Header.Set("Accept", tt.acceptHeader) + } + rr := httptest.NewRecorder() + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != tt.expectedStatusCode { + t.Errorf("handler returned wrong status code: got %v want %v", status, tt.expectedStatusCode) + } + }) + } +} diff --git a/api/server/util.go b/api/server/middleware/util.go similarity index 95% rename from api/server/util.go rename to api/server/middleware/util.go index 5fbcd9eaef..1eeb431b10 100644 --- a/api/server/util.go +++ b/api/server/middleware/util.go @@ -1,4 +1,4 @@ -package server +package middleware import ( "net/url" diff --git a/api/server/util_test.go b/api/server/middleware/util_test.go similarity index 96% rename from api/server/util_test.go rename to api/server/middleware/util_test.go index c717de19ab..5f400ce049 100644 --- a/api/server/util_test.go +++ b/api/server/middleware/util_test.go @@ -1,4 +1,4 @@ -package server +package middleware import ( "testing" diff --git a/api/server/middleware_test.go b/api/server/middleware_test.go deleted file mode 100644 index e0f823861b..0000000000 --- a/api/server/middleware_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package server - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/prysmaticlabs/prysm/v5/testing/require" -) - -func TestNormalizeQueryValuesHandler(t *testing.T) { - nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte("next handler")) - require.NoError(t, err) - }) - - handler := NormalizeQueryValuesHandler(nextHandler) - - tests := []struct { - name string - inputQuery string - expectedQuery string - }{ - { - name: "3 values", - inputQuery: "key=value1,value2,value3", - expectedQuery: "key=value1&key=value2&key=value3", // replace with expected normalized value - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - req, err := http.NewRequest("GET", "/test?"+test.inputQuery, nil) - if err != nil { - t.Fatal(err) - } - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK) - } - - if req.URL.RawQuery != test.expectedQuery { - t.Errorf("query not normalized: got %v want %v", req.URL.RawQuery, test.expectedQuery) - } - - if rr.Body.String() != "next handler" { - t.Errorf("next handler was not executed") - } - }) - } -} diff --git a/beacon-chain/node/BUILD.bazel b/beacon-chain/node/BUILD.bazel index 114f8e759e..8fe6c4b29d 100644 --- a/beacon-chain/node/BUILD.bazel +++ b/beacon-chain/node/BUILD.bazel @@ -16,7 +16,7 @@ go_library( ], deps = [ "//api/gateway:go_default_library", - "//api/server:go_default_library", + "//api/server/middleware:go_default_library", "//async/event:go_default_library", "//beacon-chain/blockchain:go_default_library", "//beacon-chain/builder:go_default_library", diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index 5c498a7175..6e126a8509 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -20,7 +20,7 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" apigateway "github.com/prysmaticlabs/prysm/v5/api/gateway" - "github.com/prysmaticlabs/prysm/v5/api/server" + "github.com/prysmaticlabs/prysm/v5/api/server/middleware" "github.com/prysmaticlabs/prysm/v5/async/event" "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v5/beacon-chain/builder" @@ -409,8 +409,8 @@ func newRouter(cliCtx *cli.Context) *mux.Router { allowedOrigins = strings.Split(flags.GPRCGatewayCorsDomain.Value, ",") } r := mux.NewRouter() - r.Use(server.NormalizeQueryValuesHandler) - r.Use(server.CorsHandler(allowedOrigins)) + r.Use(middleware.NormalizeQueryValuesHandler) + r.Use(middleware.CorsHandler(allowedOrigins)) return r } diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 97e28d2051..b71d0ba51d 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -10,6 +10,8 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//api:go_default_library", + "//api/server/middleware:go_default_library", "//beacon-chain/blockchain:go_default_library", "//beacon-chain/builder:go_default_library", "//beacon-chain/cache:go_default_library", diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 9809833c23..c086fb6eae 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -3,6 +3,11 @@ package rpc import ( "net/http" + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prysmaticlabs/prysm/v5/api" + "github.com/prysmaticlabs/prysm/v5/api/server/middleware" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/beacon" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/blob" @@ -23,10 +28,25 @@ import ( ) type endpoint struct { - template string - name string - handler http.HandlerFunc - methods []string + template string + name string + middleware []mux.MiddlewareFunc + handler http.HandlerFunc + methods []string +} + +func (e *endpoint) handlerWithMiddleware() http.HandlerFunc { + handler := http.Handler(e.handler) + for _, m := range e.middleware { + handler = m(handler) + } + return promhttp.InstrumentHandlerDuration( + httpRequestLatency.MustCurryWith(prometheus.Labels{"endpoint": e.name}), + promhttp.InstrumentHandlerCounter( + httpRequestCount.MustCurryWith(prometheus.Labels{"endpoint": e.name}), + handler, + ), + ) } func (s *Service) endpoints( @@ -73,20 +93,31 @@ func (s *Service) rewardsEndpoints(blocker lookup.Blocker, stater lookup.Stater, { template: "/eth/v1/beacon/rewards/blocks/{block_id}", name: namespace + ".BlockRewards", - handler: server.BlockRewards, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.BlockRewards, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/rewards/attestations/{epoch}", name: namespace + ".AttestationRewards", - handler: server.AttestationRewards, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.AttestationRewards, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/rewards/sync_committee/{block_id}", name: namespace + ".SyncCommitteeRewards", - handler: server.SyncCommitteeRewards, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SyncCommitteeRewards, + methods: []string{http.MethodPost}, }, } } @@ -103,8 +134,11 @@ func (s *Service) builderEndpoints(stater lookup.Stater) []endpoint { { template: "/eth/v1/builder/states/{state_id}/expected_withdrawals", name: namespace + ".ExpectedWithdrawals", - handler: server.ExpectedWithdrawals, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.ExpectedWithdrawals, + methods: []string{http.MethodGet}, }, } } @@ -119,8 +153,11 @@ func (s *Service) blobEndpoints(blocker lookup.Blocker) []endpoint { { template: "/eth/v1/beacon/blob_sidecars/{block_id}", name: namespace + ".Blobs", - handler: server.Blobs, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.Blobs, + methods: []string{http.MethodGet}, }, } } @@ -157,110 +194,173 @@ func (s *Service) validatorEndpoints( { template: "/eth/v1/validator/aggregate_attestation", name: namespace + ".GetAggregateAttestation", - handler: server.GetAggregateAttestation, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAggregateAttestation, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/contribution_and_proofs", name: namespace + ".SubmitContributionAndProofs", - handler: server.SubmitContributionAndProofs, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitContributionAndProofs, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/aggregate_and_proofs", name: namespace + ".SubmitAggregateAndProofs", - handler: server.SubmitAggregateAndProofs, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAggregateAndProofs, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/sync_committee_contribution", name: namespace + ".ProduceSyncCommitteeContribution", - handler: server.ProduceSyncCommitteeContribution, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ProduceSyncCommitteeContribution, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/sync_committee_subscriptions", name: namespace + ".SubmitSyncCommitteeSubscription", - handler: server.SubmitSyncCommitteeSubscription, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitSyncCommitteeSubscription, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/beacon_committee_subscriptions", name: namespace + ".SubmitBeaconCommitteeSubscription", - handler: server.SubmitBeaconCommitteeSubscription, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitBeaconCommitteeSubscription, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/attestation_data", name: namespace + ".GetAttestationData", - handler: server.GetAttestationData, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAttestationData, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/register_validator", name: namespace + ".RegisterValidator", - handler: server.RegisterValidator, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.RegisterValidator, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/duties/attester/{epoch}", name: namespace + ".GetAttesterDuties", - handler: server.GetAttesterDuties, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAttesterDuties, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/duties/proposer/{epoch}", name: namespace + ".GetProposerDuties", - handler: server.GetProposerDuties, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetProposerDuties, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/duties/sync/{epoch}", name: namespace + ".GetSyncCommitteeDuties", - handler: server.GetSyncCommitteeDuties, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetSyncCommitteeDuties, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/prepare_beacon_proposer", name: namespace + ".PrepareBeaconProposer", - handler: server.PrepareBeaconProposer, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.PrepareBeaconProposer, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/liveness/{epoch}", name: namespace + ".GetLiveness", - handler: server.GetLiveness, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetLiveness, + methods: []string{http.MethodPost}, }, { template: "/eth/v2/validator/blocks/{slot}", name: namespace + ".ProduceBlockV2", - handler: server.ProduceBlockV2, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.ProduceBlockV2, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/blinded_blocks/{slot}", name: namespace + ".ProduceBlindedBlock", - handler: server.ProduceBlindedBlock, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.ProduceBlindedBlock, + methods: []string{http.MethodGet}, }, { template: "/eth/v3/validator/blocks/{slot}", name: namespace + ".ProduceBlockV3", - handler: server.ProduceBlockV3, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.ProduceBlockV3, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/validator/beacon_committee_selections", name: namespace + ".BeaconCommitteeSelections", - handler: server.BeaconCommitteeSelections, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + }, + handler: server.BeaconCommitteeSelections, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/validator/sync_committee_selections", name: namespace + ".SyncCommittee Selections", - handler: server.SyncCommitteeSelections, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + }, + handler: server.SyncCommitteeSelections, + methods: []string{http.MethodPost}, }, } } @@ -284,44 +384,65 @@ func (s *Service) nodeEndpoints() []endpoint { { template: "/eth/v1/node/syncing", name: namespace + ".GetSyncStatus", - handler: server.GetSyncStatus, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetSyncStatus, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/identity", name: namespace + ".GetIdentity", - handler: server.GetIdentity, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetIdentity, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/peers/{peer_id}", name: namespace + ".GetPeer", - handler: server.GetPeer, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetPeer, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/peers", name: namespace + ".GetPeers", - handler: server.GetPeers, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetPeers, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/peer_count", name: namespace + ".GetPeerCount", - handler: server.GetPeerCount, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetPeerCount, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/version", name: namespace + ".GetVersion", - handler: server.GetVersion, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetVersion, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/node/health", name: namespace + ".GetHealth", - handler: server.GetHealth, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetHealth, + methods: []string{http.MethodGet}, }, } } @@ -365,188 +486,293 @@ func (s *Service) beaconEndpoints( { template: "/eth/v1/beacon/states/{state_id}/committees", name: namespace + ".GetCommittees", - handler: server.GetCommittees, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetCommittees, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/fork", name: namespace + ".GetStateFork", - handler: server.GetStateFork, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetStateFork, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/root", name: namespace + ".GetStateRoot", - handler: server.GetStateRoot, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetStateRoot, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/sync_committees", name: namespace + ".GetSyncCommittees", - handler: server.GetSyncCommittees, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetSyncCommittees, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/randao", name: namespace + ".GetRandao", - handler: server.GetRandao, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetRandao, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/blocks", name: namespace + ".PublishBlock", - handler: server.PublishBlock, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.PublishBlock, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/blinded_blocks", name: namespace + ".PublishBlindedBlock", - handler: server.PublishBlindedBlock, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.PublishBlindedBlock, + methods: []string{http.MethodPost}, }, { template: "/eth/v2/beacon/blocks", name: namespace + ".PublishBlockV2", - handler: server.PublishBlockV2, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.PublishBlockV2, + methods: []string{http.MethodPost}, }, { template: "/eth/v2/beacon/blinded_blocks", name: namespace + ".PublishBlindedBlockV2", - handler: server.PublishBlindedBlockV2, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.PublishBlindedBlockV2, + methods: []string{http.MethodPost}, }, { template: "/eth/v2/beacon/blocks/{block_id}", name: namespace + ".GetBlockV2", - handler: server.GetBlockV2, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetBlockV2, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/blocks/{block_id}/attestations", name: namespace + ".GetBlockAttestations", - handler: server.GetBlockAttestations, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetBlockAttestations, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/blinded_blocks/{block_id}", name: namespace + ".GetBlindedBlock", - handler: server.GetBlindedBlock, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetBlindedBlock, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/blocks/{block_id}/root", name: namespace + ".GetBlockRoot", - handler: server.GetBlockRoot, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetBlockRoot, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/attestations", name: namespace + ".ListAttestations", - handler: server.ListAttestations, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ListAttestations, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/attestations", name: namespace + ".SubmitAttestations", - handler: server.SubmitAttestations, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAttestations, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/pool/voluntary_exits", name: namespace + ".ListVoluntaryExits", - handler: server.ListVoluntaryExits, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ListVoluntaryExits, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/voluntary_exits", name: namespace + ".SubmitVoluntaryExit", - handler: server.SubmitVoluntaryExit, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitVoluntaryExit, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/pool/sync_committees", name: namespace + ".SubmitSyncCommitteeSignatures", - handler: server.SubmitSyncCommitteeSignatures, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitSyncCommitteeSignatures, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/pool/bls_to_execution_changes", name: namespace + ".ListBLSToExecutionChanges", - handler: server.ListBLSToExecutionChanges, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ListBLSToExecutionChanges, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/bls_to_execution_changes", name: namespace + ".SubmitBLSToExecutionChanges", - handler: server.SubmitBLSToExecutionChanges, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitBLSToExecutionChanges, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/pool/attester_slashings", name: namespace + ".GetAttesterSlashings", - handler: server.GetAttesterSlashings, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAttesterSlashings, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/attester_slashings", name: namespace + ".SubmitAttesterSlashing", - handler: server.SubmitAttesterSlashing, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAttesterSlashing, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/pool/proposer_slashings", name: namespace + ".GetProposerSlashings", - handler: server.GetProposerSlashings, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetProposerSlashings, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/pool/proposer_slashings", name: namespace + ".SubmitProposerSlashing", - handler: server.SubmitProposerSlashing, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitProposerSlashing, + methods: []string{http.MethodPost}, }, { template: "/eth/v1/beacon/headers", name: namespace + ".GetBlockHeaders", - handler: server.GetBlockHeaders, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetBlockHeaders, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/headers/{block_id}", name: namespace + ".GetBlockHeader", - handler: server.GetBlockHeader, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetBlockHeader, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/genesis", name: namespace + ".GetGenesis", - handler: server.GetGenesis, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetGenesis, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/finality_checkpoints", name: namespace + ".GetFinalityCheckpoints", - handler: server.GetFinalityCheckpoints, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetFinalityCheckpoints, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/validators", name: namespace + ".GetValidators", - handler: server.GetValidators, - methods: []string{http.MethodGet, http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidators, + methods: []string{http.MethodGet, http.MethodPost}, }, { template: "/eth/v1/beacon/states/{state_id}/validators/{validator_id}", name: namespace + ".GetValidator", - handler: server.GetValidator, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidator, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/validator_balances", name: namespace + ".GetValidatorBalances", - handler: server.GetValidatorBalances, - methods: []string{http.MethodGet, http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidatorBalances, + methods: []string{http.MethodGet, http.MethodPost}, }, } } @@ -557,20 +783,29 @@ func (s *Service) configEndpoints() []endpoint { { template: "/eth/v1/config/deposit_contract", name: namespace + ".GetDepositContract", - handler: config.GetDepositContract, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: config.GetDepositContract, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/config/fork_schedule", name: namespace + ".GetForkSchedule", - handler: config.GetForkSchedule, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: config.GetForkSchedule, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/config/spec", name: namespace + ".GetSpec", - handler: config.GetSpec, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: config.GetSpec, + methods: []string{http.MethodGet}, }, } } @@ -587,26 +822,38 @@ func (s *Service) lightClientEndpoints(blocker lookup.Blocker, stater lookup.Sta { template: "/eth/v1/beacon/light_client/bootstrap/{block_root}", name: namespace + ".GetLightClientBootstrap", - handler: server.GetLightClientBootstrap, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetLightClientBootstrap, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/light_client/updates", name: namespace + ".GetLightClientUpdatesByRange", - handler: server.GetLightClientUpdatesByRange, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetLightClientUpdatesByRange, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/light_client/finality_update", name: namespace + ".GetLightClientFinalityUpdate", - handler: server.GetLightClientFinalityUpdate, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetLightClientFinalityUpdate, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/light_client/optimistic_update", name: namespace + ".GetLightClientOptimisticUpdate", - handler: server.GetLightClientOptimisticUpdate, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetLightClientOptimisticUpdate, + methods: []string{http.MethodGet}, }, } } @@ -628,20 +875,29 @@ func (s *Service) debugEndpoints(stater lookup.Stater) []endpoint { { template: "/eth/v2/debug/beacon/states/{state_id}", name: namespace + ".GetBeaconStateV2", - handler: server.GetBeaconStateV2, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), + }, + handler: server.GetBeaconStateV2, + methods: []string{http.MethodGet}, }, { template: "/eth/v2/debug/beacon/heads", name: namespace + ".GetForkChoiceHeadsV2", - handler: server.GetForkChoiceHeadsV2, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetForkChoiceHeadsV2, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/debug/fork_choice", name: namespace + ".GetForkChoice", - handler: server.GetForkChoice, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetForkChoice, + methods: []string{http.MethodGet}, }, } } @@ -659,8 +915,11 @@ func (s *Service) eventsEndpoints() []endpoint { { template: "/eth/v1/events", name: namespace + ".StreamEvents", - handler: server.StreamEvents, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.EventStreamMediaType}), + }, + handler: server.StreamEvents, + methods: []string{http.MethodGet}, }, } } @@ -685,20 +944,29 @@ func (s *Service) prysmBeaconEndpoints(ch *stategen.CanonicalHistory, stater loo { template: "/prysm/v1/beacon/weak_subjectivity", name: namespace + ".GetWeakSubjectivity", - handler: server.GetWeakSubjectivity, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetWeakSubjectivity, + methods: []string{http.MethodGet}, }, { template: "/eth/v1/beacon/states/{state_id}/validator_count", name: namespace + ".GetValidatorCount", - handler: server.GetValidatorCount, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidatorCount, + methods: []string{http.MethodGet}, }, { template: "/prysm/v1/beacon/states/{state_id}/validator_count", name: namespace + ".GetValidatorCount", - handler: server.GetValidatorCount, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidatorCount, + methods: []string{http.MethodGet}, }, } } @@ -721,38 +989,58 @@ func (s *Service) prysmNodeEndpoints() []endpoint { { template: "/prysm/node/trusted_peers", name: namespace + ".ListTrustedPeer", - handler: server.ListTrustedPeer, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ListTrustedPeer, + methods: []string{http.MethodGet}, }, { template: "/prysm/v1/node/trusted_peers", name: namespace + ".ListTrustedPeer", - handler: server.ListTrustedPeer, - methods: []string{http.MethodGet}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.ListTrustedPeer, + methods: []string{http.MethodGet}, }, { template: "/prysm/node/trusted_peers", name: namespace + ".AddTrustedPeer", - handler: server.AddTrustedPeer, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.AddTrustedPeer, + methods: []string{http.MethodPost}, }, { template: "/prysm/v1/node/trusted_peers", name: namespace + ".AddTrustedPeer", - handler: server.AddTrustedPeer, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.AddTrustedPeer, + methods: []string{http.MethodPost}, }, { template: "/prysm/node/trusted_peers/{peer_id}", name: namespace + ".RemoveTrustedPeer", - handler: server.RemoveTrustedPeer, - methods: []string{http.MethodDelete}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.RemoveTrustedPeer, + methods: []string{http.MethodDelete}, }, { template: "/prysm/v1/node/trusted_peers/{peer_id}", name: namespace + ".RemoveTrustedPeer", - handler: server.RemoveTrustedPeer, - methods: []string{http.MethodDelete}, + middleware: []mux.MiddlewareFunc{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.RemoveTrustedPeer, + methods: []string{http.MethodDelete}, }, } } @@ -767,14 +1055,22 @@ func (s *Service) prysmValidatorEndpoints(coreService *core.Service, stater look { template: "/prysm/validators/performance", name: namespace + ".GetValidatorPerformance", - handler: server.GetValidatorPerformance, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidatorPerformance, + methods: []string{http.MethodPost}, }, { template: "/prysm/v1/validators/performance", name: namespace + ".GetValidatorPerformance", - handler: server.GetValidatorPerformance, - methods: []string{http.MethodPost}, + middleware: []mux.MiddlewareFunc{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetValidatorPerformance, + methods: []string{http.MethodPost}, }, } } diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 39ce4ca2c3..e30fb09aef 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -16,7 +16,6 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v5/beacon-chain/builder" "github.com/prysmaticlabs/prysm/v5/beacon-chain/cache" @@ -311,13 +310,7 @@ func NewService(ctx context.Context, cfg *Config) *Service { for _, e := range endpoints { s.cfg.Router.HandleFunc( e.template, - promhttp.InstrumentHandlerDuration( - httpRequestLatency.MustCurryWith(prometheus.Labels{"endpoint": e.name}), - promhttp.InstrumentHandlerCounter( - httpRequestCount.MustCurryWith(prometheus.Labels{"endpoint": e.name}), - e.handler, - ), - ), + e.handlerWithMiddleware(), ).Methods(e.methods...) } diff --git a/validator/node/BUILD.bazel b/validator/node/BUILD.bazel index 759ad23a59..c8b5611c24 100644 --- a/validator/node/BUILD.bazel +++ b/validator/node/BUILD.bazel @@ -37,7 +37,7 @@ go_library( deps = [ "//api:go_default_library", "//api/gateway:go_default_library", - "//api/server:go_default_library", + "//api/server/middleware:go_default_library", "//async/event:go_default_library", "//cmd:go_default_library", "//cmd/validator/flags:go_default_library", diff --git a/validator/node/node.go b/validator/node/node.go index aee6e841f7..7091c9d778 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -25,7 +25,7 @@ import ( fastssz "github.com/prysmaticlabs/fastssz" "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/gateway" - "github.com/prysmaticlabs/prysm/v5/api/server" + "github.com/prysmaticlabs/prysm/v5/api/server/middleware" "github.com/prysmaticlabs/prysm/v5/async/event" "github.com/prysmaticlabs/prysm/v5/cmd" "github.com/prysmaticlabs/prysm/v5/cmd/validator/flags" @@ -155,8 +155,8 @@ func newRouter(cliCtx *cli.Context) *mux.Router { allowedOrigins = strings.Split(flags.GRPCGatewayCorsDomain.Value, ",") } r := mux.NewRouter() - r.Use(server.NormalizeQueryValuesHandler) - r.Use(server.CorsHandler(allowedOrigins)) + r.Use(middleware.NormalizeQueryValuesHandler) + r.Use(middleware.CorsHandler(allowedOrigins)) return r }