Restructure API Middleware (#9663)

* Restructure API Middleware

* fix package name in tests

* build file

* gzl

* fix one more test
This commit is contained in:
Radosław Kapka 2021-09-23 22:41:04 +02:00 committed by GitHub
parent a9a4bb9163
commit 7dd99de69f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 408 additions and 345 deletions

View File

@ -3,12 +3,8 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"api_middleware.go",
"api_middleware_processing.go",
"api_middleware_structs.go",
"gateway.go",
"log.go",
"param_handling.go",
],
importpath = "github.com/prysmaticlabs/prysm/api/gateway",
visibility = [
@ -16,16 +12,13 @@ go_library(
"//validator:__subpackages__",
],
deps = [
"//api/grpc:go_default_library",
"//encoding/bytesutil:go_default_library",
"//api/gateway/apimiddleware:go_default_library",
"//runtime:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_rs_cors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//connectivity:go_default_library",
"@org_golang_google_grpc//credentials:go_default_library",
@ -34,14 +27,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"api_middleware_processing_test.go",
"gateway_test.go",
"param_handling_test.go",
],
srcs = ["gateway_test.go"],
embed = [":go_default_library"],
deps = [
"//api/grpc:go_default_library",
"//api/gateway/apimiddleware:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",

View File

@ -0,0 +1,40 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"api_middleware.go",
"log.go",
"param_handling.go",
"process_field.go",
"process_request.go",
"structs.go",
],
importpath = "github.com/prysmaticlabs/prysm/api/gateway/apimiddleware",
visibility = ["//visibility:public"],
deps = [
"//api/grpc:go_default_library",
"//encoding/bytesutil:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"param_handling_test.go",
"process_request_test.go",
],
embed = [":go_default_library"],
deps = [
"//api/grpc:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
],
)

View File

@ -1,4 +1,4 @@
package gateway
package apimiddleware
import (
"net/http"
@ -50,19 +50,16 @@ type QueryParam struct {
Enum bool
}
// Hook is a function that can be invoked at various stages of the request/response cycle, leading to custom behaviour for a specific endpoint.
type Hook = func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson
// CustomHandler is a function that can be invoked at the very beginning of the request,
// essentially replacing the whole default request/response logic with custom logic for a specific endpoint.
type CustomHandler = func(m *ApiProxyMiddleware, endpoint Endpoint, w http.ResponseWriter, req *http.Request) (handled bool)
// HookCollection contains hooks that can be used to amend the default request/response cycle with custom logic for a specific endpoint.
type HookCollection struct {
OnPreDeserializeRequestBodyIntoContainer []Hook
OnPostDeserializeRequestBodyIntoContainer []Hook
OnPreDeserializeGrpcResponseBodyIntoContainer []func([]byte, interface{}) (bool, ErrorJson)
OnPreSerializeMiddlewareResponseIntoJson []func(interface{}) (bool, []byte, ErrorJson)
OnPreDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson
OnPostDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson
OnPreDeserializeGrpcResponseBodyIntoContainer func([]byte, interface{}) (bool, ErrorJson)
OnPreSerializeMiddlewareResponseIntoJson func(interface{}) (bool, []byte, ErrorJson)
}
// fieldProcessor applies the processing function f to a value when the tag is present on the field.
@ -93,23 +90,10 @@ func (m *ApiProxyMiddleware) handleApiPath(gatewayRouter *mux.Router, path strin
}
if req.Method == "POST" {
for _, hook := range endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer {
if errJson := hook(*endpoint, w, req); errJson != nil {
WriteError(w, errJson, nil)
return
}
}
if errJson := DeserializeRequestBodyIntoContainer(req.Body, endpoint.PostRequest); errJson != nil {
if errJson := deserializeRequestBodyIntoContainerWrapped(endpoint, req, w); errJson != nil {
WriteError(w, errJson, nil)
return
}
for _, hook := range endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer {
if errJson := hook(*endpoint, w, req); errJson != nil {
WriteError(w, errJson, nil)
return
}
}
if errJson := ProcessRequestContainerFields(endpoint.PostRequest); errJson != nil {
WriteError(w, errJson, nil)
@ -125,87 +109,116 @@ func (m *ApiProxyMiddleware) handleApiPath(gatewayRouter *mux.Router, path strin
WriteError(w, errJson, nil)
return
}
grpcResponse, errJson := ProxyRequest(req)
grpcResp, errJson := ProxyRequest(req)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
grpcResponseBody, errJson := ReadGrpcResponseBody(grpcResponse.Body)
grpcRespBody, errJson := ReadGrpcResponseBody(grpcResp.Body)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
var responseJson []byte
if !GrpcResponseIsEmpty(grpcResponseBody) {
if errJson := DeserializeGrpcResponseBodyIntoErrorJson(endpoint.Err, grpcResponseBody); errJson != nil {
var respJson []byte
if !GrpcResponseIsEmpty(grpcRespBody) {
if errJson := DeserializeGrpcResponseBodyIntoErrorJson(endpoint.Err, grpcRespBody); errJson != nil {
WriteError(w, errJson, nil)
return
}
if endpoint.Err.Msg() != "" {
HandleGrpcResponseError(endpoint.Err, grpcResponse, w)
HandleGrpcResponseError(endpoint.Err, grpcResp, w)
return
}
var response interface{}
var resp interface{}
if req.Method == "GET" {
response = endpoint.GetResponse
resp = endpoint.GetResponse
} else {
response = endpoint.PostResponse
resp = endpoint.PostResponse
}
runDefault := true
for _, hook := range endpoint.Hooks.OnPreDeserializeGrpcResponseBodyIntoContainer {
ok, errJson := hook(grpcResponseBody, response)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
if ok {
runDefault = false
break
}
if errJson := deserializeGrpcResponseBodyIntoContainerWrapped(endpoint, grpcRespBody, resp); errJson != nil {
WriteError(w, errJson, nil)
return
}
if runDefault {
if errJson := DeserializeGrpcResponseBodyIntoContainer(grpcResponseBody, response); errJson != nil {
WriteError(w, errJson, nil)
return
}
}
if errJson := ProcessMiddlewareResponseFields(response); errJson != nil {
if errJson := ProcessMiddlewareResponseFields(resp); errJson != nil {
WriteError(w, errJson, nil)
return
}
var ok bool
runDefault = true
for _, hook := range endpoint.Hooks.OnPreSerializeMiddlewareResponseIntoJson {
ok, responseJson, errJson = hook(response)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
if ok {
runDefault = false
break
}
}
if runDefault {
responseJson, errJson = SerializeMiddlewareResponseIntoJson(response)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
respJson, errJson = serializeMiddlewareResponseIntoJsonWrapped(endpoint, respJson, resp)
if errJson != nil {
WriteError(w, errJson, nil)
return
}
}
if errJson := WriteMiddlewareResponseHeadersAndBody(grpcResponse, responseJson, w); errJson != nil {
if errJson := WriteMiddlewareResponseHeadersAndBody(grpcResp, respJson, w); errJson != nil {
WriteError(w, errJson, nil)
return
}
if errJson := Cleanup(grpcResponse.Body); errJson != nil {
if errJson := Cleanup(grpcResp.Body); errJson != nil {
WriteError(w, errJson, nil)
return
}
})
}
func deserializeRequestBodyIntoContainerWrapped(endpoint *Endpoint, req *http.Request, w http.ResponseWriter) ErrorJson {
if endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer != nil {
if errJson := endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer(*endpoint, w, req); errJson != nil {
return errJson
}
}
if errJson := DeserializeRequestBodyIntoContainer(req.Body, endpoint.PostRequest); errJson != nil {
WriteError(w, errJson, nil)
return errJson
}
if endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer != nil {
if errJson := endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer(*endpoint, w, req); errJson != nil {
return errJson
}
}
return nil
}
func deserializeGrpcResponseBodyIntoContainerWrapped(endpoint *Endpoint, grpcResponseBody []byte, resp interface{}) ErrorJson {
runDefault := true
if endpoint.Hooks.OnPreDeserializeGrpcResponseBodyIntoContainer != nil {
ok, errJson := endpoint.Hooks.OnPreDeserializeGrpcResponseBodyIntoContainer(grpcResponseBody, resp)
if errJson != nil {
return errJson
}
if ok {
runDefault = false
}
}
if runDefault {
if errJson := DeserializeGrpcResponseBodyIntoContainer(grpcResponseBody, resp); errJson != nil {
return errJson
}
}
return nil
}
func serializeMiddlewareResponseIntoJsonWrapped(endpoint *Endpoint, respJson []byte, resp interface{}) ([]byte, ErrorJson) {
runDefault := true
var errJson ErrorJson
if endpoint.Hooks.OnPreSerializeMiddlewareResponseIntoJson != nil {
var ok bool
ok, respJson, errJson = endpoint.Hooks.OnPreSerializeMiddlewareResponseIntoJson(resp)
if errJson != nil {
return nil, errJson
}
if ok {
runDefault = false
}
}
if runDefault {
respJson, errJson = SerializeMiddlewareResponseIntoJson(resp)
if errJson != nil {
return nil, errJson
}
}
return respJson, nil
}

View File

@ -0,0 +1,5 @@
package apimiddleware
import "github.com/sirupsen/logrus"
var log = logrus.WithField("prefix", "apimiddleware")

View File

@ -1,4 +1,4 @@
package gateway
package apimiddleware
import (
"encoding/base64"

View File

@ -1,4 +1,4 @@
package gateway
package apimiddleware
import (
"bytes"

View File

@ -0,0 +1,108 @@
package apimiddleware
import (
"encoding/base64"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/wealdtech/go-bytesutil"
)
// processField calls each processor function on any field that has the matching tag set.
// It is a recursive function.
func processField(s interface{}, processors []fieldProcessor) error {
kind := reflect.TypeOf(s).Kind()
if kind != reflect.Ptr && kind != reflect.Slice && kind != reflect.Array {
return fmt.Errorf("processing fields of kind '%v' is unsupported", kind)
}
t := reflect.TypeOf(s).Elem()
v := reflect.Indirect(reflect.ValueOf(s))
for i := 0; i < t.NumField(); i++ {
switch v.Field(i).Kind() {
case reflect.Slice:
sliceElem := t.Field(i).Type.Elem()
kind := sliceElem.Kind()
// Recursively process slices to struct pointers.
if kind == reflect.Ptr && sliceElem.Elem().Kind() == reflect.Struct {
for j := 0; j < v.Field(i).Len(); j++ {
if err := processField(v.Field(i).Index(j).Interface(), processors); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
// Process each string in string slices.
if kind == reflect.String {
for _, proc := range processors {
_, hasTag := t.Field(i).Tag.Lookup(proc.tag)
if hasTag {
for j := 0; j < v.Field(i).Len(); j++ {
if err := proc.f(v.Field(i).Index(j)); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
}
}
// Recursively process struct pointers.
case reflect.Ptr:
if v.Field(i).Elem().Kind() == reflect.Struct {
if err := processField(v.Field(i).Interface(), processors); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
default:
field := t.Field(i)
for _, proc := range processors {
if _, hasTag := field.Tag.Lookup(proc.tag); hasTag {
if err := proc.f(v.Field(i)); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
}
}
return nil
}
func hexToBase64Processor(v reflect.Value) error {
b, err := bytesutil.FromHexString(v.String())
if err != nil {
return err
}
v.SetString(base64.StdEncoding.EncodeToString(b))
return nil
}
func base64ToHexProcessor(v reflect.Value) error {
if v.String() == "" {
return nil
}
b, err := base64.StdEncoding.DecodeString(v.String())
if err != nil {
return err
}
v.SetString(hexutil.Encode(b))
return nil
}
func enumToLowercaseProcessor(v reflect.Value) error {
v.SetString(strings.ToLower(v.String()))
return nil
}
func timeToUnixProcessor(v reflect.Value) error {
t, err := time.Parse(time.RFC3339, v.String())
if err != nil {
return err
}
v.SetString(strconv.FormatUint(uint64(t.Unix()), 10))
return nil
}

View File

@ -1,22 +1,16 @@
package gateway
package apimiddleware
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"reflect"
"strconv"
"strings"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/api/grpc"
"github.com/wealdtech/go-bytesutil"
)
// DeserializeRequestBodyIntoContainer deserializes the request's body into an endpoint-specific struct.
@ -229,97 +223,3 @@ func Cleanup(grpcResponseBody io.ReadCloser) ErrorJson {
}
return nil
}
// processField calls each processor function on any field that has the matching tag set.
// It is a recursive function.
func processField(s interface{}, processors []fieldProcessor) error {
kind := reflect.TypeOf(s).Kind()
if kind != reflect.Ptr && kind != reflect.Slice && kind != reflect.Array {
return fmt.Errorf("processing fields of kind '%v' is unsupported", kind)
}
t := reflect.TypeOf(s).Elem()
v := reflect.Indirect(reflect.ValueOf(s))
for i := 0; i < t.NumField(); i++ {
switch v.Field(i).Kind() {
case reflect.Slice:
sliceElem := t.Field(i).Type.Elem()
kind := sliceElem.Kind()
// Recursively process slices to struct pointers.
if kind == reflect.Ptr && sliceElem.Elem().Kind() == reflect.Struct {
for j := 0; j < v.Field(i).Len(); j++ {
if err := processField(v.Field(i).Index(j).Interface(), processors); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
// Process each string in string slices.
if kind == reflect.String {
for _, proc := range processors {
_, hasTag := t.Field(i).Tag.Lookup(proc.tag)
if hasTag {
for j := 0; j < v.Field(i).Len(); j++ {
if err := proc.f(v.Field(i).Index(j)); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
}
}
// Recursively process struct pointers.
case reflect.Ptr:
if v.Field(i).Elem().Kind() == reflect.Struct {
if err := processField(v.Field(i).Interface(), processors); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
default:
field := t.Field(i)
for _, proc := range processors {
if _, hasTag := field.Tag.Lookup(proc.tag); hasTag {
if err := proc.f(v.Field(i)); err != nil {
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
}
}
}
}
}
return nil
}
func hexToBase64Processor(v reflect.Value) error {
b, err := bytesutil.FromHexString(v.String())
if err != nil {
return err
}
v.SetString(base64.StdEncoding.EncodeToString(b))
return nil
}
func base64ToHexProcessor(v reflect.Value) error {
if v.String() == "" {
return nil
}
b, err := base64.StdEncoding.DecodeString(v.String())
if err != nil {
return err
}
v.SetString(hexutil.Encode(b))
return nil
}
func enumToLowercaseProcessor(v reflect.Value) error {
v.SetString(strings.ToLower(v.String()))
return nil
}
func timeToUnixProcessor(v reflect.Value) error {
t, err := time.Parse(time.RFC3339, v.String())
if err != nil {
return err
}
v.SetString(strconv.FormatUint(uint64(t.Unix()), 10))
return nil
}

View File

@ -1,4 +1,4 @@
package gateway
package apimiddleware
import (
"bytes"

View File

@ -1,4 +1,4 @@
package gateway
package apimiddleware
import (
"net/http"

View File

@ -13,6 +13,7 @@ import (
"github.com/gorilla/mux"
gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/runtime"
"github.com/rs/cors"
"google.golang.org/grpc"
@ -46,7 +47,7 @@ type Gateway struct {
cancel context.CancelFunc
remoteCert string
gatewayAddr string
apiMiddlewareEndpointFactory EndpointFactory
apiMiddlewareEndpointFactory apimiddleware.EndpointFactory
ctx context.Context
startFailure error
remoteAddr string
@ -98,7 +99,7 @@ func (g *Gateway) WithMaxCallRecvMsgSize(size uint64) *Gateway {
}
// WithApiMiddleware allows adding API Middleware proxy to the gateway.
func (g *Gateway) WithApiMiddleware(endpointFactory EndpointFactory) *Gateway {
func (g *Gateway) WithApiMiddleware(endpointFactory apimiddleware.EndpointFactory) *Gateway {
g.apiMiddlewareEndpointFactory = endpointFactory
return g
}
@ -271,7 +272,7 @@ func (g *Gateway) dialUnix(ctx context.Context, addr string) (*grpc.ClientConn,
}
func (g *Gateway) registerApiMiddleware() {
proxy := &ApiProxyMiddleware{
proxy := &apimiddleware.ApiProxyMiddleware{
GatewayAddress: g.gatewayAddr,
EndpointCreator: g.apiMiddlewareEndpointFactory,
}

View File

@ -10,6 +10,7 @@ import (
"testing"
"github.com/gorilla/mux"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@ -24,7 +25,7 @@ func (*mockEndpointFactory) Paths() []string {
return []string{}
}
func (*mockEndpointFactory) Create(_ string) (*Endpoint, error) {
func (*mockEndpointFactory) Create(_ string) (*apimiddleware.Endpoint, error) {
return nil, nil
}

View File

@ -12,7 +12,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//api/gateway:go_default_library",
"//api/gateway/apimiddleware:go_default_library",
"//api/grpc:go_default_library",
"//beacon-chain/rpc/eth/events:go_default_library",
"//encoding/bytesutil:go_default_library",
@ -32,7 +32,7 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//api/gateway:go_default_library",
"//api/gateway/apimiddleware:go_default_library",
"//api/grpc:go_default_library",
"//beacon-chain/rpc/eth/events:go_default_library",
"//encoding/bytesutil:go_default_library",

View File

@ -11,7 +11,7 @@ import (
"strconv"
"strings"
"github.com/prysmaticlabs/prysm/api/gateway"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/api/grpc"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/eth/events"
"github.com/r3labs/sse"
@ -23,7 +23,7 @@ type sszConfig struct {
responseJson sszResponseJson
}
func handleGetBeaconStateSSZ(m *gateway.ApiProxyMiddleware, endpoint gateway.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
func handleGetBeaconStateSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
sszPath: "/eth/v1/debug/beacon/states/{state_id}/ssz",
fileName: "beacon_state.ssz",
@ -32,7 +32,7 @@ func handleGetBeaconStateSSZ(m *gateway.ApiProxyMiddleware, endpoint gateway.End
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconBlockSSZ(m *gateway.ApiProxyMiddleware, endpoint gateway.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
func handleGetBeaconBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
sszPath: "/eth/v1/beacon/blocks/{block_id}/ssz",
fileName: "beacon_block.ssz",
@ -41,7 +41,7 @@ func handleGetBeaconBlockSSZ(m *gateway.ApiProxyMiddleware, endpoint gateway.End
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconStateSSZV2(m *gateway.ApiProxyMiddleware, endpoint gateway.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
func handleGetBeaconStateSSZV2(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
sszPath: "/eth/v2/debug/beacon/states/{state_id}/ssz",
fileName: "beacon_state.ssz",
@ -50,7 +50,7 @@ func handleGetBeaconStateSSZV2(m *gateway.ApiProxyMiddleware, endpoint gateway.E
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconBlockSSZV2(m *gateway.ApiProxyMiddleware, endpoint gateway.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
func handleGetBeaconBlockSSZV2(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
sszPath: "/eth/v2/beacon/blocks/{block_id}/ssz",
fileName: "beacon_block.ssz",
@ -60,8 +60,8 @@ func handleGetBeaconBlockSSZV2(m *gateway.ApiProxyMiddleware, endpoint gateway.E
}
func handleGetSSZ(
m *gateway.ApiProxyMiddleware,
endpoint gateway.Endpoint,
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
w http.ResponseWriter,
req *http.Request,
config sszConfig,
@ -71,42 +71,42 @@ func handleGetSSZ(
}
if errJson := prepareSSZRequestForProxying(m, endpoint, req, config.sszPath); errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
return true
}
grpcResponse, errJson := gateway.ProxyRequest(req)
grpcResponse, errJson := apimiddleware.ProxyRequest(req)
if errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
return true
}
grpcResponseBody, errJson := gateway.ReadGrpcResponseBody(grpcResponse.Body)
grpcResponseBody, errJson := apimiddleware.ReadGrpcResponseBody(grpcResponse.Body)
if errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
return true
}
if errJson := gateway.DeserializeGrpcResponseBodyIntoErrorJson(endpoint.Err, grpcResponseBody); errJson != nil {
gateway.WriteError(w, errJson, nil)
if errJson := apimiddleware.DeserializeGrpcResponseBodyIntoErrorJson(endpoint.Err, grpcResponseBody); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
if endpoint.Err.Msg() != "" {
gateway.HandleGrpcResponseError(endpoint.Err, grpcResponse, w)
apimiddleware.HandleGrpcResponseError(endpoint.Err, grpcResponse, w)
return true
}
if errJson := gateway.DeserializeGrpcResponseBodyIntoContainer(grpcResponseBody, config.responseJson); errJson != nil {
gateway.WriteError(w, errJson, nil)
if errJson := apimiddleware.DeserializeGrpcResponseBodyIntoContainer(grpcResponseBody, config.responseJson); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
responseSsz, errJson := serializeMiddlewareResponseIntoSSZ(config.responseJson.SSZData())
if errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
return true
}
if errJson := writeSSZResponseHeaderAndBody(grpcResponse, w, responseSsz, config.fileName); errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
return true
}
if errJson := gateway.Cleanup(grpcResponse.Body); errJson != nil {
gateway.WriteError(w, errJson, nil)
if errJson := apimiddleware.Cleanup(grpcResponse.Body); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
@ -126,12 +126,16 @@ func sszRequested(req *http.Request) bool {
return false
}
func prepareSSZRequestForProxying(m *gateway.ApiProxyMiddleware, endpoint gateway.Endpoint, req *http.Request, sszPath string) gateway.ErrorJson {
func prepareSSZRequestForProxying(
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
req *http.Request, sszPath string,
) apimiddleware.ErrorJson {
req.URL.Scheme = "http"
req.URL.Host = m.GatewayAddress
req.RequestURI = ""
req.URL.Path = sszPath
if errJson := gateway.HandleURLParameters(endpoint.Path, req, []string{}); errJson != nil {
if errJson := apimiddleware.HandleURLParameters(endpoint.Path, req, []string{}); errJson != nil {
return errJson
}
// We have to add the prefix after handling parameters because adding the prefix changes URL segment indexing.
@ -139,16 +143,16 @@ func prepareSSZRequestForProxying(m *gateway.ApiProxyMiddleware, endpoint gatewa
return nil
}
func serializeMiddlewareResponseIntoSSZ(data string) (sszResponse []byte, errJson gateway.ErrorJson) {
func serializeMiddlewareResponseIntoSSZ(data string) (sszResponse []byte, errJson apimiddleware.ErrorJson) {
// Serialize the SSZ part of the deserialized value.
b, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil, gateway.InternalServerErrorWithMessage(err, "could not decode response body into base64")
return nil, apimiddleware.InternalServerErrorWithMessage(err, "could not decode response body into base64")
}
return b, nil
}
func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWriter, responseSsz []byte, fileName string) gateway.ErrorJson {
func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWriter, responseSsz []byte, fileName string) apimiddleware.ErrorJson {
var statusCodeHeader string
for h, vs := range grpcResp.Header {
// We don't want to expose any gRPC metadata in the HTTP response, so we skip forwarding metadata headers.
@ -165,7 +169,7 @@ func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWrite
if statusCodeHeader != "" {
code, err := strconv.Atoi(statusCodeHeader)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not parse status code")
return apimiddleware.InternalServerErrorWithMessage(err, "could not parse status code")
}
w.WriteHeader(code)
} else {
@ -175,12 +179,12 @@ func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWrite
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename="+fileName)
if _, err := io.Copy(w, ioutil.NopCloser(bytes.NewReader(responseSsz))); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not write response message")
return apimiddleware.InternalServerErrorWithMessage(err, "could not write response message")
}
return nil
}
func handleEvents(m *gateway.ApiProxyMiddleware, _ gateway.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
func handleEvents(m *apimiddleware.ApiProxyMiddleware, _ apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
sseClient := sse.NewClient("http://" + m.GatewayAddress + "/internal" + req.URL.RequestURI())
eventChan := make(chan *sse.Event)
@ -188,21 +192,21 @@ func handleEvents(m *gateway.ApiProxyMiddleware, _ gateway.Endpoint, w http.Resp
// Because of this subscribing to streams doesn't work as intended, resulting in each event being handled by all subscriptions.
// To handle events properly, we subscribe just once using a placeholder value ('events') and handle all topics inside this subscription.
if err := sseClient.SubscribeChan("events", eventChan); err != nil {
gateway.WriteError(w, gateway.InternalServerError(err), nil)
apimiddleware.WriteError(w, apimiddleware.InternalServerError(err), nil)
sseClient.Unsubscribe(eventChan)
return
}
errJson := receiveEvents(eventChan, w, req)
if errJson != nil {
gateway.WriteError(w, errJson, nil)
apimiddleware.WriteError(w, errJson, nil)
}
sseClient.Unsubscribe(eventChan)
return true
}
func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
for {
select {
case msg := <-eventChan:
@ -225,11 +229,11 @@ func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http
// and assign the attestation back to event data for further processing.
eventData := &aggregatedAttReceivedDataJson{}
if err := json.Unmarshal(msg.Data, eventData); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
attData, err := json.Marshal(eventData.Aggregate)
if err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
msg.Data = attData
case events.VoluntaryExitTopic:
@ -241,7 +245,7 @@ func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http
case "error":
data = &eventErrorJson{}
default:
return &gateway.DefaultErrorJson{
return &apimiddleware.DefaultErrorJson{
Message: fmt.Sprintf("Event type '%s' not supported", string(msg.Event)),
Code: http.StatusInternalServerError,
}
@ -259,14 +263,14 @@ func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http
}
}
func writeEvent(msg *sse.Event, w http.ResponseWriter, data interface{}) gateway.ErrorJson {
func writeEvent(msg *sse.Event, w http.ResponseWriter, data interface{}) apimiddleware.ErrorJson {
if err := json.Unmarshal(msg.Data, data); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
if errJson := gateway.ProcessMiddlewareResponseFields(data); errJson != nil {
if errJson := apimiddleware.ProcessMiddlewareResponseFields(data); errJson != nil {
return errJson
}
dataJson, errJson := gateway.SerializeMiddlewareResponseIntoJson(data)
dataJson, errJson := apimiddleware.SerializeMiddlewareResponseIntoJson(data)
if errJson != nil {
return errJson
}
@ -274,28 +278,28 @@ func writeEvent(msg *sse.Event, w http.ResponseWriter, data interface{}) gateway
w.Header().Set("Content-Type", "text/event-stream")
if _, err := w.Write([]byte("event: ")); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
if _, err := w.Write(msg.Event); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
if _, err := w.Write([]byte("\ndata: ")); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
if _, err := w.Write(dataJson); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
if _, err := w.Write([]byte("\n\n")); err != nil {
return gateway.InternalServerError(err)
return apimiddleware.InternalServerError(err)
}
return nil
}
func flushEvent(w http.ResponseWriter) gateway.ErrorJson {
func flushEvent(w http.ResponseWriter) apimiddleware.ErrorJson {
flusher, ok := w.(http.Flusher)
if !ok {
return &gateway.DefaultErrorJson{Message: fmt.Sprintf("Flush not supported in %T", w), Code: http.StatusInternalServerError}
return &apimiddleware.DefaultErrorJson{Message: fmt.Sprintf("Flush not supported in %T", w), Code: http.StatusInternalServerError}
}
flusher.Flush()
return nil

View File

@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/prysmaticlabs/prysm/api/gateway"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/api/grpc"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/eth/events"
"github.com/prysmaticlabs/prysm/testing/assert"
@ -48,10 +48,10 @@ func TestSSZRequested(t *testing.T) {
}
func TestPrepareSSZRequestForProxying(t *testing.T) {
middleware := &gateway.ApiProxyMiddleware{
GatewayAddress: "http://gateway.example",
middleware := &apimiddleware.ApiProxyMiddleware{
GatewayAddress: "http://apimiddleware.example",
}
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
Path: "http://foo.example",
}
var body bytes.Buffer

View File

@ -9,23 +9,23 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/api/gateway"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
)
// https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapAttestationsArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapAttestationsArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitAttestationRequestJson); ok {
atts := make([]*attestationJson, 0)
if err := json.NewDecoder(req.Body).Decode(&atts); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitAttestationRequestJson{Data: atts}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -34,16 +34,16 @@ func wrapAttestationsArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req
// Some endpoints e.g. https://ethereum.github.io/beacon-apis/#/Validator/getAttesterDuties expect posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with an 'Index' field.
func wrapValidatorIndicesArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapValidatorIndicesArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*dutiesRequestJson); ok {
indices := make([]string, 0)
if err := json.NewDecoder(req.Body).Decode(&indices); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &dutiesRequestJson{Index: indices}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -52,16 +52,16 @@ func wrapValidatorIndicesArray(endpoint gateway.Endpoint, _ http.ResponseWriter,
// https://ethereum.github.io/beacon-apis/#/Validator/publishAggregateAndProofs expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapSignedAggregateAndProofArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapSignedAggregateAndProofArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitAggregateAndProofsRequestJson); ok {
data := make([]*signedAggregateAttestationAndProofJson, 0)
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitAggregateAndProofsRequestJson{Data: data}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -70,16 +70,16 @@ func wrapSignedAggregateAndProofArray(endpoint gateway.Endpoint, _ http.Response
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconCommitteeSubnet expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapBeaconCommitteeSubscriptionsArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapBeaconCommitteeSubscriptionsArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitBeaconCommitteeSubscriptionsRequestJson); ok {
data := make([]*beaconCommitteeSubscribeJson, 0)
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitBeaconCommitteeSubscriptionsRequestJson{Data: data}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -88,16 +88,16 @@ func wrapBeaconCommitteeSubscriptionsArray(endpoint gateway.Endpoint, _ http.Res
// https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapSyncCommitteeSubscriptionsArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapSyncCommitteeSubscriptionsArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitSyncCommitteeSubscriptionRequestJson); ok {
data := make([]*syncCommitteeSubscriptionJson, 0)
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitSyncCommitteeSubscriptionRequestJson{Data: data}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -106,16 +106,16 @@ func wrapSyncCommitteeSubscriptionsArray(endpoint gateway.Endpoint, _ http.Respo
// https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapSyncCommitteeSignaturesArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapSyncCommitteeSignaturesArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitSyncCommitteeSignaturesRequestJson); ok {
data := make([]*syncCommitteeMessageJson, 0)
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitSyncCommitteeSignaturesRequestJson{Data: data}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -124,16 +124,16 @@ func wrapSyncCommitteeSignaturesArray(endpoint gateway.Endpoint, _ http.Response
// https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapSignedContributionAndProofsArray(endpoint gateway.Endpoint, _ http.ResponseWriter, req *http.Request) gateway.ErrorJson {
func wrapSignedContributionAndProofsArray(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request) apimiddleware.ErrorJson {
if _, ok := endpoint.PostRequest.(*submitContributionAndProofsRequestJson); ok {
data := make([]*signedContributionAndProofJson, 0)
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not decode body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &submitContributionAndProofsRequestJson{Data: data}
b, err := json.Marshal(j)
if err != nil {
return gateway.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
return apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
}
@ -141,7 +141,7 @@ func wrapSignedContributionAndProofsArray(endpoint gateway.Endpoint, _ http.Resp
}
// Posted graffiti needs to have length of 32 bytes, but client is allowed to send data of any length.
func prepareGraffiti(endpoint gateway.Endpoint, _ http.ResponseWriter, _ *http.Request) gateway.ErrorJson {
func prepareGraffiti(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
if block, ok := endpoint.PostRequest.(*signedBeaconBlockContainerJson); ok {
b := bytesutil.ToBytes32([]byte(block.Message.Body.Graffiti))
block.Message.Body.Graffiti = hexutil.Encode(b[:])
@ -164,14 +164,14 @@ type tempSyncSubcommitteeValidatorsJson struct {
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.0.0#/Beacon/getEpochSyncCommittees returns validator_aggregates as a nested array.
// grpc-gateway returns a struct with nested fields which we have to transform into a plain 2D array.
func prepareValidatorAggregates(body []byte, responseContainer interface{}) (bool, gateway.ErrorJson) {
func prepareValidatorAggregates(body []byte, responseContainer interface{}) (bool, apimiddleware.ErrorJson) {
tempContainer := &tempSyncCommitteesResponseJson{}
if err := json.Unmarshal(body, tempContainer); err != nil {
return false, gateway.InternalServerErrorWithMessage(err, "could not unmarshal response into temp container")
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not unmarshal response into temp container")
}
container, ok := responseContainer.(*syncCommitteesResponseJson)
if !ok {
return false, gateway.InternalServerError(errors.New("container is not of the correct type"))
return false, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
container.Data = &syncCommitteeValidatorsJson{}
@ -196,10 +196,10 @@ type altairBlockResponseJson struct {
Data *signedBeaconBlockAltairContainerJson `json:"data"`
}
func serializeV2Block(response interface{}) (bool, []byte, gateway.ErrorJson) {
func serializeV2Block(response interface{}) (bool, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*blockV2ResponseJson)
if !ok {
return false, nil, gateway.InternalServerError(errors.New("container is not of the correct type"))
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
@ -223,7 +223,7 @@ func serializeV2Block(response interface{}) (bool, []byte, gateway.ErrorJson) {
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, gateway.InternalServerErrorWithMessage(err, "could not marshal response")
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return true, j, nil
}
@ -238,10 +238,10 @@ type altairStateResponseJson struct {
Data *beaconStateV2Json `json:"data"`
}
func serializeV2State(response interface{}) (bool, []byte, gateway.ErrorJson) {
func serializeV2State(response interface{}) (bool, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*beaconStateV2ResponseJson)
if !ok {
return false, nil, gateway.InternalServerError(errors.New("container is not of the correct type"))
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
@ -259,7 +259,7 @@ func serializeV2State(response interface{}) (bool, []byte, gateway.ErrorJson) {
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, gateway.InternalServerErrorWithMessage(err, "could not marshal response")
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return true, j, nil
}
@ -274,10 +274,10 @@ type altairProduceBlockResponseJson struct {
Data *beaconBlockAltairJson `json:"data"`
}
func serializeProducedV2Block(response interface{}) (bool, []byte, gateway.ErrorJson) {
func serializeProducedV2Block(response interface{}) (bool, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*produceBlockResponseV2Json)
if !ok {
return false, nil, gateway.InternalServerError(errors.New("container is not of the correct type"))
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
@ -295,7 +295,7 @@ func serializeProducedV2Block(response interface{}) (bool, []byte, gateway.Error
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, gateway.InternalServerErrorWithMessage(err, "could not marshal response")
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return true, j, nil
}

View File

@ -9,7 +9,7 @@ import (
"testing"
"github.com/gogo/protobuf/types"
"github.com/prysmaticlabs/prysm/api/gateway"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
"github.com/prysmaticlabs/prysm/testing/assert"
@ -18,7 +18,7 @@ import (
func TestWrapAttestationArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitAttestationRequestJson{},
}
unwrappedAtts := []*attestationJson{{AggregationBits: "1010"}}
@ -39,7 +39,7 @@ func TestWrapAttestationArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitAttestationRequestJson{},
}
var body bytes.Buffer
@ -56,7 +56,7 @@ func TestWrapAttestationArray(t *testing.T) {
func TestWrapValidatorIndicesArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &dutiesRequestJson{},
}
unwrappedIndices := []string{"1", "2"}
@ -80,7 +80,7 @@ func TestWrapValidatorIndicesArray(t *testing.T) {
func TestWrapSignedAggregateAndProofArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitAggregateAndProofsRequestJson{},
}
unwrappedAggs := []*signedAggregateAttestationAndProofJson{{Signature: "sig"}}
@ -101,7 +101,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitAggregateAndProofsRequestJson{},
}
var body bytes.Buffer
@ -118,7 +118,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) {
func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{},
}
unwrappedSubs := []*beaconCommitteeSubscribeJson{{
@ -149,7 +149,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{},
}
var body bytes.Buffer
@ -166,7 +166,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitSyncCommitteeSubscriptionRequestJson{},
}
unwrappedSubs := []*syncCommitteeSubscriptionJson{
@ -202,7 +202,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitSyncCommitteeSubscriptionRequestJson{},
}
var body bytes.Buffer
@ -219,7 +219,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitSyncCommitteeSignaturesRequestJson{},
}
unwrappedSigs := []*syncCommitteeMessageJson{{
@ -248,7 +248,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitSyncCommitteeSignaturesRequestJson{},
}
var body bytes.Buffer
@ -265,7 +265,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
func TestWrapSignedContributionAndProofsArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitContributionAndProofsRequestJson{},
}
unwrapped := []*signedContributionAndProofJson{
@ -315,7 +315,7 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) {
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &submitContributionAndProofsRequestJson{},
}
var body bytes.Buffer
@ -331,7 +331,7 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) {
}
func TestPrepareGraffiti(t *testing.T) {
endpoint := gateway.Endpoint{
endpoint := apimiddleware.Endpoint{
PostRequest: &signedBeaconBlockContainerJson{
Message: &beaconBlockJson{
Body: &beaconBlockBodyJson{},

View File

@ -2,7 +2,7 @@ package apimiddleware
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/api/gateway"
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
)
// BeaconEndpointFactory creates endpoints used for running beacon chain API calls through the API Middleware.
@ -67,8 +67,8 @@ func (f *BeaconEndpointFactory) Paths() []string {
}
// Create returns a new endpoint for the provided API path.
func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
endpoint := gateway.DefaultEndpoint()
func (f *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, error) {
endpoint := apimiddleware.DefaultEndpoint()
switch path {
case "/eth/v1/beacon/genesis":
endpoint.GetResponse = &genesisResponseJson{}
@ -79,52 +79,52 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
case "/eth/v1/beacon/states/{state_id}/finality_checkpoints":
endpoint.GetResponse = &stateFinalityCheckpointResponseJson{}
case "/eth/v1/beacon/states/{state_id}/validators":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "id", Hex: true}, {Name: "status", Enum: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "id", Hex: true}, {Name: "status", Enum: true}}
endpoint.GetResponse = &stateValidatorsResponseJson{}
case "/eth/v1/beacon/states/{state_id}/validators/{validator_id}":
endpoint.GetResponse = &stateValidatorResponseJson{}
case "/eth/v1/beacon/states/{state_id}/validator_balances":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "id", Hex: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "id", Hex: true}}
endpoint.GetResponse = &validatorBalancesResponseJson{}
case "/eth/v1/beacon/states/{state_id}/committees":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "epoch"}, {Name: "index"}, {Name: "slot"}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "epoch"}, {Name: "index"}, {Name: "slot"}}
endpoint.GetResponse = &stateCommitteesResponseJson{}
case "/eth/v1/beacon/states/{state_id}/sync_committees":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "epoch"}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "epoch"}}
endpoint.GetResponse = &syncCommitteesResponseJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeGrpcResponseBodyIntoContainer: []func([]byte, interface{}) (bool, gateway.ErrorJson){prepareValidatorAggregates},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeGrpcResponseBodyIntoContainer: prepareValidatorAggregates,
}
case "/eth/v1/beacon/headers":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "slot"}, {Name: "parent_root", Hex: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "slot"}, {Name: "parent_root", Hex: true}}
endpoint.GetResponse = &blockHeadersResponseJson{}
case "/eth/v1/beacon/headers/{block_id}":
endpoint.GetResponse = &blockHeaderResponseJson{}
case "/eth/v1/beacon/blocks":
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
endpoint.Hooks = gateway.HookCollection{
OnPostDeserializeRequestBodyIntoContainer: []gateway.Hook{prepareGraffiti},
endpoint.Hooks = apimiddleware.HookCollection{
OnPostDeserializeRequestBodyIntoContainer: prepareGraffiti,
}
case "/eth/v1/beacon/blocks/{block_id}":
endpoint.GetResponse = &blockResponseJson{}
endpoint.CustomHandlers = []gateway.CustomHandler{handleGetBeaconBlockSSZ}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZ}
case "/eth/v2/beacon/blocks/{block_id}":
endpoint.GetResponse = &blockV2ResponseJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: []func(interface{}) (bool, []byte, gateway.ErrorJson){serializeV2Block},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeV2Block,
}
endpoint.CustomHandlers = []gateway.CustomHandler{handleGetBeaconBlockSSZV2}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZV2}
case "/eth/v1/beacon/blocks/{block_id}/root":
endpoint.GetResponse = &blockRootResponseJson{}
case "/eth/v1/beacon/blocks/{block_id}/attestations":
endpoint.GetResponse = &blockAttestationsResponseJson{}
case "/eth/v1/beacon/pool/attestations":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "slot"}, {Name: "committee_index"}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "slot"}, {Name: "committee_index"}}
endpoint.GetResponse = &attestationsPoolResponseJson{}
endpoint.PostRequest = &submitAttestationRequestJson{}
endpoint.Err = &indexedVerificationFailureErrorJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapAttestationsArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapAttestationsArray,
}
case "/eth/v1/beacon/pool/attester_slashings":
endpoint.PostRequest = &attesterSlashingJson{}
@ -138,13 +138,13 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
case "/eth/v1/beacon/pool/sync_committees":
endpoint.PostRequest = &submitSyncCommitteeSignaturesRequestJson{}
endpoint.Err = &indexedVerificationFailureErrorJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapSyncCommitteeSignaturesArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSyncCommitteeSignaturesArray,
}
case "/eth/v1/node/identity":
endpoint.GetResponse = &identityResponseJson{}
case "/eth/v1/node/peers":
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "state", Enum: true}, {Name: "direction", Enum: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "state", Enum: true}, {Name: "direction", Enum: true}}
endpoint.GetResponse = &peersResponseJson{}
case "/eth/v1/node/peers/{peer_id}":
endpoint.RequestURLLiterals = []string{"peer_id"}
@ -159,13 +159,13 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
// Use default endpoint
case "/eth/v1/debug/beacon/states/{state_id}":
endpoint.GetResponse = &beaconStateResponseJson{}
endpoint.CustomHandlers = []gateway.CustomHandler{handleGetBeaconStateSSZ}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZ}
case "/eth/v2/debug/beacon/states/{state_id}":
endpoint.GetResponse = &beaconStateV2ResponseJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: []func(interface{}) (bool, []byte, gateway.ErrorJson){serializeV2State},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeV2State,
}
endpoint.CustomHandlers = []gateway.CustomHandler{handleGetBeaconStateSSZV2}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZV2}
case "/eth/v1/debug/beacon/heads":
endpoint.GetResponse = &forkChoiceHeadsResponseJson{}
case "/eth/v1/config/fork_schedule":
@ -175,13 +175,13 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
case "/eth/v1/config/spec":
endpoint.GetResponse = &specResponseJson{}
case "/eth/v1/events":
endpoint.CustomHandlers = []gateway.CustomHandler{handleEvents}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleEvents}
case "/eth/v1/validator/duties/attester/{epoch}":
endpoint.PostRequest = &dutiesRequestJson{}
endpoint.PostResponse = &attesterDutiesResponseJson{}
endpoint.RequestURLLiterals = []string{"epoch"}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapValidatorIndicesArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapValidatorIndicesArray,
}
case "/eth/v1/validator/duties/proposer/{epoch}":
endpoint.GetResponse = &proposerDutiesResponseJson{}
@ -190,48 +190,48 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
endpoint.PostRequest = &dutiesRequestJson{}
endpoint.PostResponse = &syncCommitteeDutiesResponseJson{}
endpoint.RequestURLLiterals = []string{"epoch"}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapValidatorIndicesArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapValidatorIndicesArray,
}
case "/eth/v1/validator/blocks/{slot}":
endpoint.GetResponse = &produceBlockResponseJson{}
endpoint.RequestURLLiterals = []string{"slot"}
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "randao_reveal", Hex: true}, {Name: "graffiti", Hex: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "randao_reveal", Hex: true}, {Name: "graffiti", Hex: true}}
case "/eth/v2/validator/blocks/{slot}":
endpoint.GetResponse = &produceBlockResponseV2Json{}
endpoint.RequestURLLiterals = []string{"slot"}
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "randao_reveal", Hex: true}, {Name: "graffiti", Hex: true}}
endpoint.Hooks = gateway.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: []func(interface{}) (bool, []byte, gateway.ErrorJson){serializeProducedV2Block},
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "randao_reveal", Hex: true}, {Name: "graffiti", Hex: true}}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeProducedV2Block,
}
case "/eth/v1/validator/attestation_data":
endpoint.GetResponse = &produceAttestationDataResponseJson{}
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "slot"}, {Name: "committee_index"}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "slot"}, {Name: "committee_index"}}
case "/eth/v1/validator/aggregate_attestation":
endpoint.GetResponse = &aggregateAttestationResponseJson{}
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "attestation_data_root", Hex: true}, {Name: "slot"}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "attestation_data_root", Hex: true}, {Name: "slot"}}
case "/eth/v1/validator/beacon_committee_subscriptions":
endpoint.PostRequest = &submitBeaconCommitteeSubscriptionsRequestJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapBeaconCommitteeSubscriptionsArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapBeaconCommitteeSubscriptionsArray,
}
case "/eth/v1/validator/sync_committee_subscriptions":
endpoint.PostRequest = &submitSyncCommitteeSubscriptionRequestJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapSyncCommitteeSubscriptionsArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSyncCommitteeSubscriptionsArray,
}
case "/eth/v1/validator/aggregate_and_proofs":
endpoint.PostRequest = &submitAggregateAndProofsRequestJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapSignedAggregateAndProofArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSignedAggregateAndProofArray,
}
case "/eth/v1/validator/sync_committee_contribution":
endpoint.GetResponse = &produceSyncCommitteeContributionResponseJson{}
endpoint.RequestQueryParams = []gateway.QueryParam{{Name: "slot"}, {Name: "subcommittee_index"}, {Name: "beacon_block_root", Hex: true}}
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "slot"}, {Name: "subcommittee_index"}, {Name: "beacon_block_root", Hex: true}}
case "/eth/v1/validator/contribution_and_proofs":
endpoint.PostRequest = &submitContributionAndProofsRequestJson{}
endpoint.Hooks = gateway.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: []gateway.Hook{wrapSignedContributionAndProofsArray},
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSignedContributionAndProofsArray,
}
default:
return nil, errors.New("invalid path")

View File

@ -1,6 +1,8 @@
package apimiddleware
import "github.com/prysmaticlabs/prysm/api/gateway"
import (
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
)
// genesisResponseJson is used in /beacon/genesis API endpoint.
type genesisResponseJson struct {
@ -739,7 +741,7 @@ type eventChainReorgJson struct {
// indexedVerificationFailureErrorJson is a JSON representation of the error returned when verifying an indexed object.
type indexedVerificationFailureErrorJson struct {
gateway.DefaultErrorJson
apimiddleware.DefaultErrorJson
Failures []*singleIndexedVerificationFailureJson `json:"failures"`
}