diff --git a/client/internal/request/request.go b/client/internal/request/request.go index a736c578..22d3f64c 100644 --- a/client/internal/request/request.go +++ b/client/internal/request/request.go @@ -818,7 +818,11 @@ func (r *Request) writeStreamPayload(mediaType string, producers map[string]runt // The same reasoning applies to the form/multipart branch. func (r *Request) writeNonStreamPayload(mediaType string, producers map[string]runtime.Producer) (io.Reader, error) { r.header.Set(runtime.HeaderContentType, mediaType) - producer := producers[mediaType] + producer, ok := producers[mediaType] + if !ok { + return nil, fmt.Errorf("no producer registered for content type %q (register one with Runtime.Producers)", mediaType) + } + if err := producer.Produce(r.buf, r.payload); err != nil { return nil, err } diff --git a/client/internal/request/request_test.go b/client/internal/request/request_test.go index c5d157eb..6fc7de04 100644 --- a/client/internal/request/request_test.go +++ b/client/internal/request/request_test.go @@ -236,6 +236,28 @@ func TestBuildRequest_BuildHTTP_Payload(t *testing.T) { assert.Equal(t, append(expectedBody, '\n'), actualBody) } +// TestBuildRequest_BuildHTTP_UnregisteredProducer guards against a +// regression of go-swagger/go-swagger#3192: a non-stream payload sent +// with a media type that has no producer registered must surface an +// actionable error rather than panic on a nil-method dispatch. +func TestBuildRequest_BuildHTTP_UnregisteredProducer(t *testing.T) { + bd := []struct{ Name, Hobby string }{{valTom, valOregonTrail}} + reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { + _ = req.SetBodyParam(bd) + return nil + }) + r := New(http.MethodPost, "/flats/", reqWrtr) + + const unregisteredMime = "application/x-not-registered" + req, cancel, err := r.BuildHTTPContext(t.Context(), unregisteredMime, "", testProducers, nil, nil) + t.Cleanup(cancel) + + require.Error(t, err) + assert.Nil(t, req) + assert.Contains(t, err.Error(), unregisteredMime) + assert.Contains(t, err.Error(), "no producer registered") +} + func TestBuildRequest_BuildHTTP_SetsInAuth(t *testing.T) { bd := []struct{ Name, Hobby string }{{valTom, valOregonTrail}, {valJohn, valBirdWatching}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { diff --git a/client/runtime.go b/client/runtime.go index 604fb0ea..b890f9f4 100644 --- a/client/runtime.go +++ b/client/runtime.go @@ -95,13 +95,15 @@ func New(host, basePath string, schemes []string) *Runtime { // Enhancement proposal: https://github.com/go-openapi/runtime/issues/385 rt.Consumers = map[string]runtime.Consumer{ - runtime.YAMLMime: yamlpc.YAMLConsumer(), - runtime.JSONMime: runtime.JSONConsumer(), - runtime.XMLMime: runtime.XMLConsumer(), - runtime.TextMime: runtime.TextConsumer(), - runtime.HTMLMime: runtime.TextConsumer(), - runtime.CSVMime: runtime.CSVConsumer(), - runtime.DefaultMime: runtime.ByteStreamConsumer(), + runtime.YAMLMime: yamlpc.YAMLConsumer(), + runtime.JSONMime: runtime.JSONConsumer(), + runtime.XMLMime: runtime.XMLConsumer(), + runtime.TextMime: runtime.TextConsumer(), + runtime.HTMLMime: runtime.TextConsumer(), + runtime.CSVMime: runtime.CSVConsumer(), + runtime.MultipartFormMime: runtime.ByteStreamConsumer(), + runtime.URLencodedFormMime: runtime.ByteStreamConsumer(), + runtime.DefaultMime: runtime.ByteStreamConsumer(), } rt.Producers = map[string]runtime.Producer{ runtime.YAMLMime: yamlpc.YAMLProducer(),