Skip to content
2 changes: 0 additions & 2 deletions proto/http/http.proto
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ message Request {
// header contains the request header fields either received
// by the server or to be sent by the client
repeated Element header = 6;
// body is the request payload in bytes
bytes body = 7;
// content_length records the length of the associated content
int64 content_length = 8;
// transfer_encoding lists the transfer encodings from outermost to
Expand Down
134 changes: 62 additions & 72 deletions proto/pb/http/http.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions vms/rpcchainvm/ghttp/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"io"
"net/http"

"github.com/ava-labs/avalanchego/proto/pb/io/reader"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/greader"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils"

Expand Down Expand Up @@ -54,16 +56,11 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
server := grpcutils.NewServer()
closer.Add(server)
responsewriterpb.RegisterWriterServer(server, gresponsewriter.NewServer(w))
reader.RegisterReaderServer(server, greader.NewServer(r.Body))

// Start responsewriter gRPC service.
go grpcutils.Serve(serverListener, server)

body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

req := &httppb.HTTPRequest{
ResponseWriter: &httppb.ResponseWriter{
ServerAddr: serverListener.Addr().String(),
Expand All @@ -75,7 +72,6 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ProtoMajor: int32(r.ProtoMajor),
ProtoMinor: int32(r.ProtoMinor),
Header: make([]*httppb.Element, 0, len(r.Header)),
Body: body,
ContentLength: r.ContentLength,
TransferEncoding: r.TransferEncoding,
Host: r.Host,
Expand Down
5 changes: 4 additions & 1 deletion vms/rpcchainvm/ghttp/http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (

"google.golang.org/protobuf/types/known/emptypb"

"github.com/ava-labs/avalanchego/proto/pb/io/reader"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/greader"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter"
"github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils"

Expand Down Expand Up @@ -50,13 +52,14 @@ func (s *Server) Handle(ctx context.Context, req *httppb.HTTPRequest) (*emptypb.
}

writer := gresponsewriter.NewClient(writerHeaders, responsewriterpb.NewWriterClient(clientConn))
body := greader.NewClient(reader.NewReaderClient(clientConn))

// create the request with the current context
request, err := http.NewRequestWithContext(
ctx,
req.Request.Method,
req.Request.RequestUri,
bytes.NewBuffer(req.Request.Body),
body,
)
if err != nil {
return nil, err
Expand Down
40 changes: 40 additions & 0 deletions vms/rpcchainvm/ghttp/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@
package ghttp

import (
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/test/bufconn"

httppb "github.com/ava-labs/avalanchego/proto/pb/http"
)

var _ io.Reader = (*infiniteStream)(nil)

func TestConvertWriteResponse(t *testing.T) {
scenerios := map[string]struct {
resp *httppb.HandleSimpleHTTPResponse
Expand Down Expand Up @@ -49,3 +56,36 @@ func TestConvertWriteResponse(t *testing.T) {
})
}
}

func TestRequestClientArbitrarilyLongBody(t *testing.T) {
require := require.New(t)

listener := bufconn.Listen(0)
server := grpc.NewServer()
httppb.RegisterHTTPServer(server, &httppb.UnimplementedHTTPServer{})

go func() {
require.NoError(server.Serve(listener))
}()

conn, err := grpc.NewClient(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
require.NoError(err)

client := NewClient(httppb.NewHTTPClient(conn))

w := &httptest.ResponseRecorder{}
r := &http.Request{
Header: map[string][]string{
"Upgrade": {"foo"}, // Make this look like a streaming request
},
Body: io.NopCloser(infiniteStream{}),
}

client.ServeHTTP(w, r) // Shouldn't block forever reading the body
}

type infiniteStream struct{}

func (infiniteStream) Read(p []byte) (n int, err error) {
return len(p), nil
}