From d7103fdef302dd5f99f9a0f76a3f42f30848a9d3 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 30 Mar 2021 11:42:50 -0500 Subject: [PATCH] HTTP Request Sink Tool (#8689) * request sink test * append if not exist --- tools/http-request-sink/BUILD.bazel | 56 ++++++++++++++++++++ tools/http-request-sink/main.go | 76 ++++++++++++++++++++++++++++ tools/http-request-sink/main_test.go | 67 ++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 tools/http-request-sink/BUILD.bazel create mode 100644 tools/http-request-sink/main.go create mode 100644 tools/http-request-sink/main_test.go diff --git a/tools/http-request-sink/BUILD.bazel b/tools/http-request-sink/BUILD.bazel new file mode 100644 index 0000000000..dd6651c7b5 --- /dev/null +++ b/tools/http-request-sink/BUILD.bazel @@ -0,0 +1,56 @@ +load("@prysm//tools/go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") +load("@io_bazel_rules_docker//go:image.bzl", "go_image") +load("@io_bazel_rules_docker//container:container.bzl", "container_bundle") +load("@io_bazel_rules_docker//contrib:push-all.bzl", "docker_push") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importpath = "github.com/prysmaticlabs/prysm/tools/http-request-sink", + visibility = ["//visibility:private"], + deps = ["//shared/params:go_default_library"], +) + +go_binary( + name = "http-request-sink", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) + +go_image( + name = "image", + base = select({ + "//tools:base_image_alpine": "//tools:alpine_cc_image", + "//tools:base_image_cc": "//tools:cc_image", + "//conditions:default": "//tools:cc_image", + }), + binary = ":http-request-sink", + tags = ["manual"], + visibility = ["//visibility:private"], +) + +container_bundle( + name = "image_bundle", + images = { + "gcr.io/prysmaticlabs/prysm/http-request-sink:latest": ":image", + "gcr.io/prysmaticlabs/prysm/http-request-sink:{DOCKER_TAG}": ":image", + }, + tags = ["manual"], +) + +docker_push( + name = "push_images", + bundle = ":image_bundle", + tags = ["manual"], +) + +go_test( + name = "go_default_test", + srcs = ["main_test.go"], + embed = [":go_default_library"], + deps = [ + "//shared/params:go_default_library", + "//shared/testutil/require:go_default_library", + ], +) diff --git a/tools/http-request-sink/main.go b/tools/http-request-sink/main.go new file mode 100644 index 0000000000..9982e424d6 --- /dev/null +++ b/tools/http-request-sink/main.go @@ -0,0 +1,76 @@ +// Package main implements a simple, http-request-sink which writes +// incoming http request bodies to an append-only text file at a specified directory. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "strconv" + + "github.com/prysmaticlabs/prysm/shared/params" +) + +func main() { + port := flag.Int("port", 8080, "port to listen on") + writeDirPath := flag.String("write-dir", "", "directory to write an append-only file") + flag.Parse() + if *writeDirPath == "" { + log.Fatal("Needs a -write-dir path") + } + + // If the file doesn't exist, create it, or append to the file. + f, err := os.OpenFile( + filepath.Join(*writeDirPath, "requests.log"), + os.O_APPEND|os.O_CREATE|os.O_RDWR, + params.BeaconIoConfig().ReadWritePermissions, + ) + if err != nil { + log.Println(err) + } + defer func() { + if err = f.Close(); err != nil { + log.Fatal(err) + } + }() + + http.HandleFunc("/", func(writer http.ResponseWriter, r *http.Request) { + reqContent := map[string]interface{}{} + if err = parseRequest(r, &reqContent); err != nil { + log.Println(err) + } + log.Printf("Capturing request from %s", r.RemoteAddr) + if err = captureRequest(f, reqContent); err != nil { + log.Println(err) + } + }) + log.Printf("Listening on port %d", *port) + log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil)) +} + +func captureRequest(f *os.File, m map[string]interface{}) error { + enc, err := json.Marshal(m) + if err != nil { + return err + } + _, err = f.WriteString(fmt.Sprintf("%s\n", enc)) + return err +} + +func parseRequest(req *http.Request, unmarshalStruct interface{}) error { + body, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + if err = req.Body.Close(); err != nil { + return err + } + req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + return json.Unmarshal(body, unmarshalStruct) +} diff --git a/tools/http-request-sink/main_test.go b/tools/http-request-sink/main_test.go new file mode 100644 index 0000000000..bce8ddd5da --- /dev/null +++ b/tools/http-request-sink/main_test.go @@ -0,0 +1,67 @@ +package main + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil/require" +) + +type sampleRPCRequest struct { + Name string `json:"name"` + ETHMethod string `json:"eth_method"` + Address string `json:"address"` +} + +func Test_parseAndCaptureRequest(t *testing.T) { + tmpFile := filepath.Join(os.TempDir(), "faketest.log") + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(tmpFile)) + }) + body := &sampleRPCRequest{ + Name: "eth2", + ETHMethod: "eth2_produceBlock", + Address: "0x0923920930923", + } + enc, err := json.Marshal(body) + require.NoError(t, err) + httpReq, err := http.NewRequest("GET", "/", bytes.NewBuffer(enc)) + require.NoError(t, err) + + reqContent := map[string]interface{}{} + err = parseRequest(httpReq, &reqContent) + require.NoError(t, err) + + // If the file doesn't exist, create it, or append to the file. + f, err := os.OpenFile( + tmpFile, + os.O_APPEND|os.O_CREATE|os.O_RDWR, + params.BeaconIoConfig().ReadWritePermissions, + ) + require.NoError(t, err) + + err = captureRequest(f, reqContent) + require.NoError(t, err) + require.NoError(t, f.Close()) + + f, err = os.Open(tmpFile) + require.NoError(t, err) + fileContents, err := ioutil.ReadAll(f) + require.NoError(t, err) + + receivedContent := map[string]interface{}{} + err = json.Unmarshal(fileContents, &receivedContent) + require.NoError(t, err) + + for key, val := range reqContent { + receivedVal, ok := receivedContent[key] + require.Equal(t, true, ok) + require.DeepEqual(t, val, receivedVal) + } +}