Skip to content

Commit

Permalink
Feedback add testcases for partial data
Browse files Browse the repository at this point in the history
  • Loading branch information
emcfarlane committed Jun 27, 2023
1 parent dc18bf1 commit 1af51a1
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 11 deletions.
85 changes: 74 additions & 11 deletions connect_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"math/rand"
"net/http"
"net/http/httptest"
"path"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -2081,17 +2082,49 @@ func TestHandlerReturnsNilResponse(t *testing.T) {

func TestStreamUnexpectedEOF(t *testing.T) {
t.Parallel()
testcases := map[string]http.HandlerFunc{
"stream_unexpected_eof": func(responseWriter http.ResponseWriter, request *http.Request) {
_, _ = io.Copy(io.Discard, request.Body)
header := responseWriter.Header()
header.Set("Content-Type", "application/connect+json")
responseWriter.WriteHeader(http.StatusOK)
head := [5]byte{}
payload := []byte(`{"number": 42}`)
binary.BigEndian.PutUint32(head[1:], uint32(len(payload)))
_, _ = responseWriter.Write(head[:])
_, _ = responseWriter.Write(payload)
},
"stream_partial_payload": func(responseWriter http.ResponseWriter, request *http.Request) {
_, _ = io.Copy(io.Discard, request.Body)
header := responseWriter.Header()
header.Set("Content-Type", "application/connect+json")
responseWriter.WriteHeader(http.StatusOK)
head := [5]byte{}
payload := []byte(`{"number": 42}`)
binary.BigEndian.PutUint32(head[1:], uint32(len(payload)))
_, _ = responseWriter.Write(head[:])
_, _ = responseWriter.Write(payload[:len(payload)-1])
},
"stream_partial_frame": func(responseWriter http.ResponseWriter, request *http.Request) {
_, _ = io.Copy(io.Discard, request.Body)
header := responseWriter.Header()
header.Set("Content-Type", "application/connect+json")
responseWriter.WriteHeader(http.StatusOK)
head := [5]byte{}
payload := []byte(`{"number": 42}`)
binary.BigEndian.PutUint32(head[1:], uint32(len(payload)))
_, _ = responseWriter.Write(head[:4])
},
}

mux := http.NewServeMux()
mux.HandleFunc("/connect.ping.v1.PingService/CountUp", func(responseWriter http.ResponseWriter, request *http.Request) {
io.Copy(io.Discard, request.Body) //nolint:errcheck
header := responseWriter.Header()
header["content-type"] = []string{"application/connect+json"}
responseWriter.WriteHeader(http.StatusOK)
head := [5]byte{0, 0, 0, 0, 0}
payload := []byte(`{"number": 42}`)
binary.BigEndian.PutUint32(head[1:], uint32(len(payload)))
responseWriter.Write(head[:]) //nolint:errcheck
responseWriter.Write(payload) //nolint:errcheck
mux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
testcase, ok := testcases[path.Base(request.Header.Get("Test-Case"))]
if !ok {
responseWriter.WriteHeader(http.StatusNotFound)
return
}
testcase(responseWriter, request)
})
server := httptest.NewUnstartedServer(mux)
server.EnableHTTP2 = true
Expand All @@ -2107,7 +2140,8 @@ func TestStreamUnexpectedEOF(t *testing.T) {
t.Parallel()
const upTo = 2
request := connect.NewRequest(&pingv1.CountUpRequest{Number: upTo})
request.Header().Set(clientHeader, headerValue)
request.Header().Set("Test-Case", t.Name())

stream, err := client.CountUp(context.Background(), request)
assert.Nil(t, err)
for stream.Receive() {
Expand All @@ -2117,6 +2151,35 @@ func TestStreamUnexpectedEOF(t *testing.T) {
assert.Equal(t, connect.CodeOf(stream.Err()), connect.CodeUnknown)
assert.True(t, errors.Is(stream.Err(), io.ErrUnexpectedEOF))
})
t.Run("stream_partial_payload", func(t *testing.T) {
t.Parallel()
const upTo = 2
request := connect.NewRequest(&pingv1.CountUpRequest{Number: upTo})
request.Header().Set("Test-Case", t.Name())
stream, err := client.CountUp(context.Background(), request)
assert.Nil(t, err)
for stream.Receive() {
assert.Equal(t, stream.Msg().Number, 42)
}
assert.NotNil(t, stream.Err())
assert.Equal(t, connect.CodeOf(stream.Err()), connect.CodeInvalidArgument)
assert.Equal(t, stream.Err().Error(), "invalid_argument: protocol error: promised 14 bytes in enveloped message, got 13 bytes")
})
t.Run("stream_partial_frame", func(t *testing.T) {
t.Parallel()
const upTo = 2
request := connect.NewRequest(&pingv1.CountUpRequest{Number: upTo})
request.Header().Set("Test-Case", t.Name())
stream, err := client.CountUp(context.Background(), request)
assert.Nil(t, err)
for stream.Receive() {
assert.Equal(t, stream.Msg().Number, 42)
}
assert.NotNil(t, stream.Err())
assert.Equal(t, connect.CodeOf(stream.Err()), connect.CodeInvalidArgument)
t.Log(stream.Err())
assert.Equal(t, stream.Err().Error(), "invalid_argument: protocol error: incomplete envelope: unexpected EOF")
})
}

// TestBlankImportCodeGeneration tests that services.connect.go is generated with
Expand Down
3 changes: 3 additions & 0 deletions envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ func (r *envelopeReader) Read(env *envelope) *Error {
// We're reading from an http.MaxBytesHandler, and we've exceeded the read limit.
return maxBytesErr
}
if err == nil {
err = io.ErrUnexpectedEOF
}
return errorf(
CodeInvalidArgument,
"protocol error: incomplete envelope: %w", err,
Expand Down

0 comments on commit 1af51a1

Please sign in to comment.