Skip to content

Commit

Permalink
Allow clients to set Host in headers (#522)
Browse files Browse the repository at this point in the history
Allows Host to set the request.Host on the client and promotes
request.Host back to a Header in the handler.

Fixes #513
  • Loading branch information
emcfarlane authored Jun 16, 2023
1 parent e0fed67 commit 29a06c7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 1 deletion.
58 changes: 58 additions & 0 deletions connect_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,64 @@ func TestHeaderBasic(t *testing.T) {
assert.Equal(t, response.Header().Get(key), hval)
}

func TestHeaderHost(t *testing.T) {
t.Parallel()
const (
key = "Host"
cval = "buf.build"
)

pingServer := &pluggablePingServer{
ping: func(_ context.Context, request *connect.Request[pingv1.PingRequest]) (*connect.Response[pingv1.PingResponse], error) {
assert.Equal(t, request.Header().Get(key), cval)
response := connect.NewResponse(&pingv1.PingResponse{})
return response, nil
},
}

newHTTP2Server := func(t *testing.T) *httptest.Server {
t.Helper()
mux := http.NewServeMux()
mux.Handle(pingv1connect.NewPingServiceHandler(pingServer))
server := httptest.NewUnstartedServer(mux)
server.EnableHTTP2 = true
server.StartTLS()
t.Cleanup(server.Close)
return server
}

callWithHost := func(t *testing.T, client pingv1connect.PingServiceClient) {
t.Helper()

request := connect.NewRequest(&pingv1.PingRequest{})
request.Header().Set(key, cval)
response, err := client.Ping(context.Background(), request)
assert.Nil(t, err)
assert.Equal(t, response.Header().Get(key), "")
}

t.Run("connect", func(t *testing.T) {
t.Parallel()
server := newHTTP2Server(t)
client := pingv1connect.NewPingServiceClient(server.Client(), server.URL)
callWithHost(t, client)
})

t.Run("grpc", func(t *testing.T) {
t.Parallel()
server := newHTTP2Server(t)
client := pingv1connect.NewPingServiceClient(server.Client(), server.URL, connect.WithGRPC())
callWithHost(t, client)
})

t.Run("grpc-web", func(t *testing.T) {
t.Parallel()
server := newHTTP2Server(t)
client := pingv1connect.NewPingServiceClient(server.Client(), server.URL, connect.WithGRPCWeb())
callWithHost(t, client)
})
}

func TestTimeoutParsing(t *testing.T) {
t.Parallel()
const timeout = 10 * time.Minute
Expand Down
5 changes: 5 additions & 0 deletions duplex_http_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ func (d *duplexHTTPCall) makeRequest() {
// on d.responseReady, so we can't race with them.
defer close(d.responseReady)

// Promote the header Host to the request object.
if host := d.request.Header.Get(headerHost); len(host) > 0 {
d.request.Host = host
}

if d.onRequestSend != nil {
d.onRequestSend(d.request)
}
Expand Down
3 changes: 2 additions & 1 deletion handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func NewBidiStreamHandler[Req, Res any](

// ServeHTTP implements [http.Handler].
func (h *Handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
// We don't need to defer functions to close the request body or read to
// We don't need to defer functions to close the request body or read to
// EOF: the stream we construct later on already does that, and we only
// return early when dealing with misbehaving clients. In those cases, it's
// okay if we can't re-use the connection.
Expand Down Expand Up @@ -221,6 +221,7 @@ func (h *Handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Re

// Establish a stream and serve the RPC.
setHeaderCanonical(request.Header, headerContentType, contentType)
setHeaderCanonical(request.Header, headerHost, request.Host)
ctx, cancel, timeoutErr := protocolHandler.SetTimeout(request) //nolint: contextcheck
if timeoutErr != nil {
ctx = request.Context()
Expand Down
1 change: 1 addition & 0 deletions protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (

const (
headerContentType = "Content-Type"
headerHost = "Host"
headerUserAgent = "User-Agent"
headerTrailer = "Trailer"

Expand Down

0 comments on commit 29a06c7

Please sign in to comment.