talkdatatome
talkdatatome

Reputation: 664

Stray characters in output when using Docker's Go SDK

I am trying to convert the io.ReadCloser (interface) that I am getting after running the Docker image via Go docker-sdk to []byte for further use.

When I read from the io.ReadCloser using stdcopy.StdCopy to stdout, it prints the data perfectly.

The code stdcopy.StdCopy(os.Stderr, os.Stdout, out) prints:

Content-Type: text/html




<html>
<head><title>HTML response</title></head>
<body><h1>Hello, Goodbye</h1></body>
</html>

Because I need to send this entire content as a Response I need to convert the content to []byte or string. But, once I convert the io.ReadCloser to []byte or string using any method but stdcopy.StdCopy, it adds a special character to some lines.

The snippet I used to read from out to buf using *bytes.Buffer.ReadFrom:

    buf := new(bytes.Buffer)
    buf.ReadFrom(out)
    fmt.Println(buf.String())

Prints:

Content-Type: text/html




<html>
*<head><title>HTML response</title></head>
%<body><h1>Hello, Goodbye</h1></body>
</html>

As you can see extra characters like * and % are being added. I have also tried ioutil.ReadAll function as well, no luck. Any suggestion would be much appreciated.

Upvotes: 1

Views: 428

Answers (1)

shmsr
shmsr

Reputation: 4204

Those are stray bytes like *, %, etc. prefixed with some of the lines.

The stray bytes appear to be a custom stream multiplexing protocol, allowing STDOUT and STDERR to be sent down the same connection.

Using stdcopy.StdCopy() interprets these custom headers and those stray characters are avoided by removing the protocol header for each piece of data.

Refer: https://github.com/moby/moby/blob/master/pkg/stdcopy/stdcopy.go#L42

// Write sends the buffer to the underneath writer.
// It inserts the prefix header before the buffer,
// so stdcopy.StdCopy knows where to multiplex the output.
// It makes stdWriter to implement io.Writer.

So, stdcopy.StdCopy is your friend and an alternative for io.Copy and friends when working with Docker.

A sample example to give you an idea:

package main

import (
    "bytes"
    "fmt"
    "io"
    "strings"
)

var resp string = `
Content-Type: text/html
<html>
<head><title>HTML response</title></head>
<body><h1>Hello, Goodbye</h1></body>
</html>`

func main() {
    src := strings.NewReader(resp)
    dst := &bytes.Buffer{}
    _, _ = io.Copy(dst, src)
    fmt.Println(dst.String())
    // Output:
    //
    // Content-Type: text/html
    // <html>
    // <head><title>HTML response</title></head>
    // <body><h1>Hello, Goodbye</h1></body>
    // </html>
}

Signature for: func io.Copy(dst io.Writer, src io.Reader) As dst (*bytes.Buffer) has a Write method and hence it implements the io.Writer interface and it works.

Now use the same idea when using stdcopy.StdCopy() as the signature is the same. https://pkg.go.dev/github.com/docker/docker/pkg/stdcopy#StdCopy

Upvotes: 5

Related Questions