goa icon indicating copy to clipboard operation
goa copied to clipboard

Allow implementing `io.WriterTo` for `SkipResponseBodyEncodeDecode` responses.

Open duckbrain opened this issue 9 months ago • 1 comments

When a server uses SkipResponseBodyEncodeDecode, the response must be of the type io.ReadCloser. In most cases I've come across, an io.Writer would be more ergonomic to use. I often find myself needing to use io.Pipe to create an io.Reader I can write to. It's made worse by the fact that it's abstracting the http.ResponseWriter and pushing it through a bufio.Reader, so there's lots of extra copying going on.

I propose to have the generated code check if the io.ReadCloser implements io.WriterTo, and if it does, call that instead.

Additionally, it'd be nice to have a helper in the goa package to implement the interface from an io.WriterTo, to make it easier to switch to, eg: If used as the response to a handler, the Read would never be called, and the pipe would never be created.

func SkipResponseWriter(wt io.WriterTo) io.ReadCloser {
	return writerToReaderAdapter{wt, nil}
}

type writerToReaderAdapter struct {
	io.WriterTo
	pr *io.PipeReader
}

func (a *writerToReaderAdapter) initPipe() {
	if a.pr != nil {
		return
	}
	r, w := io.Pipe()
	go func() {
		_, err := wt.WriteTo(w)
		w.CloseWithError(err)
	}()
	a.pr = r
}

func (a *writerToReaderAdapter) Read(b []byte) (n int, err error) {
	a.initPipe()
	return a.pr.Read(b)
}

func (a *writerToReaderAdapter) Close() error {
	a.initPipe()
	return a.pr.Close()
}

As an alternate suggestion, you could have some way in the DSL that changes the expected type.

duckbrain avatar May 22 '24 23:05 duckbrain