mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 19:50:36 +00:00
98c57e75c0
eventsource is required for the validator api. this implements the eventsource sink/server handler the implementation is based off of this document: https://html.spec.whatwg.org/multipage/server-sent-events.html note that this is a building block for the full eventsource server. there still needs to be work done prysm has their own custom solution based off of protobuf/grpc: https://hackmd.io/@prysmaticlabs/eventstream-api using that would be not good existing eventsource implementations for golang are not good for our situation. options are: 1. https://github.com/r3labs/sse - has most stars - this is the best contender, since it uses []byte and not string, but it allocates and copies extra times in the server (because of use of fprintf) and makes an incorrect assumption about Last-Event-ID needing to be a number (i can't find this in the specification). 2. https://github.com/antage/eventsource -requires full buffers, copies many times, does not provide abstraction for headers. relatively unmaintained 3. https://github.com/donovanhide/eventsource - missing functionality around sending ids, requires full buffers, etc 4. https://github.com/bernerdschaefer/eventsource - 10 years old, unmaintained. additionally, implemetations other than r3labs/sse are very incorrect because they do not split up the data field correctly when newlines are sent. (parsers by specification will fail to encode messages sent by most of these implementations that have newlines, as i understand it). the implementation by r3labs/sse is also incorrect because it does not respect \r finally, all these implementations have very heavy implementation of the server, which we do not need since we will use fixed sequence ids. r3labs/sse for instance hijacks the entire handler and ties that to the server, losing a lot of flexiblity in how we implement our server for the beacon api, we need to stream: ```head, block, attestation, voluntary_exit, bls_to_execution_change, finalized_checkpoint, chain_reorg, contribution_and_proof, light_client_finality_update, light_client_optimistic_update, payload_attributes``` some of these are rather big json payloads, and the ability to simultaneously stream them from io.Readers instead of making a full copy of the payload every time we wish to rebroadcast it will save a lot of heap size for both resource constrained environments and serving at scale. the protocol itself is relatively simple, there are just a few gotchas
41 lines
970 B
Go
41 lines
970 B
Go
package sse
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestEncoderSimple(t *testing.T) {
|
|
type testCase struct {
|
|
xs []*Packet
|
|
w string
|
|
}
|
|
cases := []testCase{{
|
|
[]*Packet{
|
|
{Event: "hello", Data: strings.NewReader("some data")},
|
|
{Data: strings.NewReader("some other data with no event header")},
|
|
},
|
|
"event: hello\ndata: some data\n\ndata: some other data with no event header\n",
|
|
},
|
|
{
|
|
[]*Packet{
|
|
{Event: "hello", Data: strings.NewReader("some \n funky\r\n data\r")},
|
|
{Data: strings.NewReader("some other data with an id"), ID: ID("dogs")},
|
|
},
|
|
"event: hello\ndata: some \ndata: funky\r\ndata: data\r\ndata: some other data with an id\nid: dogs\n",
|
|
},
|
|
}
|
|
for _, v := range cases {
|
|
buf := &bytes.Buffer{}
|
|
enc := NewEncoder(buf)
|
|
for _, p := range v.xs {
|
|
require.NoError(t, enc.Encode(p))
|
|
}
|
|
assert.EqualValues(t, v.w, buf.String())
|
|
}
|
|
}
|