2019-12-22 Weekend Learnings

December 22, 2019 • edited April 10, 2020

I understood how to use Go composition to extend the functionality similar to inheritance in OOP. I’ve used it before but now I can say that I understand it. Also, I’ve done a 3D model of the van that I’m going to use for the moving in January. Just to know if I can fit everything inside.

Go composition

I’m a few years late. This guy is explaining a similar problem to the one that I was trying to solve in his post “struct composition with go”.

The problem I was facing this week was how to record a web request body using a middleware. In my case, it was for debugging purposes but this technique can be used in similar scenarios. The structure http.Request contains a lot of fields, but we are only interested now in the Request.Body which is an io.ReadCloser

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type Request struct {
	// Body is the request's body.
	//
	// For client requests, a nil body means the request has no
	// body, such as a GET request. The HTTP Client's Transport
	// is responsible for calling the Close method.
	//
	// For server requests, the Request Body is always non-nil
	// but will return EOF immediately when no body is present.
	// The Server will close the request body. The ServeHTTP
	// Handler does not need to.
  Body io.ReadCloser
}

After Googling a little bit, I found that the package io provides a structure called io.TeeReader. This is the functionality that I’m looking for. Given a Reader and a Writer, It writes everything that is read to the writer. It’s like the tee terminal tool.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// TeeReader returns a Reader that writes to w what it reads from r.
// All reads from r performed through it are matched with
// corresponding writes to w. There is no internal buffering -
// the write must complete before the read completes.
// Any error encountered while writing is reported as a read error.
func TeeReader(r Reader, w Writer) Reader {
	return &teeReader{r, w}
}
type teeReader struct {
	r Reader
	w Writer
}

func (t *teeReader) Read(p []byte) (n int, err error) {
	n, err = t.r.Read(p)
	if n > 0 {
		if n, err := t.w.Write(p[:n]); err != nil {
			return n, err
		}
	}
	return
}

So with this, we can simply wrap the request body with a TeeReader and write it to a buffer to be used later.

1
2
	recorded := &bytes.Buffer{}
	request.Body = io.TeeReader(request.Body, recorded)

But this does not compile… The problem is that the return value of io.TeeReader is an io.Reader and the request.Body must be a io.ReadCloser. Thinking fast, we need an io.TeeReadCloser, but it doesn’t exist.

Don’t panic, we can implement it. we just need to copy/paste the io.teeReader and add the close method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func TeeReadCloser(r io.ReadCloser, w io.Writer) io.ReadCloser {
	return &teeReadCloser{r, w}
}

type teeReadCloser struct {
	r io.ReadCloser
	w io.Writer
}

func (t *teeReadCloser) Read(p []byte) (n int, err error) {
	n, err = t.r.Read(p)
	if n > 0 {
		if n, err := t.w.Write(p[:n]); err != nil {
			return n, err
		}
	}
	return
}
func (t *teeReadCloser) Close() error {
	return t.r.Close()
}

Wonderful, but this is not what I want to show you. It would be great if we could “inherit” from the TeeReader and include the Close functionality. This is how the code looks like.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func TeeReadCloser(r io.ReadCloser, w io.Writer) io.ReadCloser {
	return &teeReadCloser{
		Reader: io.TeeReader(r, w),
		Closer: r,
	}
}

type teeReadCloser struct {
	io.Reader
	io.Closer
}

With this approach, we avoid code duplication as the functionality of TeeReader is still implemented by the io.TeeReader. Our teeReadCloser is a composition of two interfaces io.Reader and io.Closer. The io.Reader is implemented by the io.TeeReader with the original io.ReadCloser and the output io.Writer. Then, The io.Closer is implemented by the io.ReadCloser passed to the constructor. This effectively redirects the teeReadCloser.Read calls to the io.TeeReader and the teeReadCloser.Close directly to the io.ReadCloser.

Another example related to this one is how to extend the implementation of http.ResponseWriter to record the response code. Again, with logging purposes. This is really simple.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func NewStatusRecorder(ResponseWriter http.ResponseWriter) *StatusRecorder {
	return &StatusRecorder{ResponseWriter: ResponseWriter}
}

type statusRecorder struct {
	http.ResponseWriter
	StatusCode int
}

func (sr *statusRecorder) WriteHeader(statusCode int) {
	sr.StatusCode = statusCode
	sr.ResponseWriter.WriteHeader(statusCode)
}

This allows us to override the ResponseWriter.WriteHeader method with our new one that just stores the StatusCode and calls the underlying method. The other two methods of the interface http.ResponseWriter are not modified. I find this approach really clean, easy to extend and easy to modify. The following is an example of how to use this implementation with a middleware.

1
2
3
4
5
6
7
func LogStatus(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		sr := NewStatusRecorder(w)
		next(sr, r)
		log.Print("Response code is %d", sr.StatusCode)
	}
}

Moving van cargo model

So here it is. All my furniture inside a moving van. I’m doing this model with furniture slightly bigger than what it actually is and leaving space between objects. The conclusion is that I think I will be able to fit everything inside a big van, so I don’t need a small truck.

Random color code:

  • Blue things are fragile. Fridge, Microwave and decoration.
  • Brown is something I will provably disassemble
  • Yellow things are the box spring.
  • Black is the sofa.

van

I didn’t put any box in this model, I hope to be able to put all the boxes in my car. In the worst case, there is still some room in the van for boxes. Let’s see…

The model is generated with Blender.

References

blogweekendgo

How to get bored

2019-12-15 Weekend Learnings