io.Copy write: broken pipe for big file

1.7k Views Asked by At

I have a file server and a proxy server through which I can access the file server via http

Simple file server :


    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "tmp/1.mp4")
    })

    if err := http.ListenAndServe("localhost:3000", nil); err != http.ErrServerClosed {
        log.Fatalf("HTTP server ListenAndServe: %v", err)
    }

and proxy server :


type Server struct {
    conn net.Conn
}

func (s *Server) Handle(w http.ResponseWriter, r *http.Request) error {
    if err := r.Write(s.conn); err != nil {
        log.Fatal("req.Write ", err)
    }

    resp, err := http.ReadResponse(bufio.NewReader(s.conn), r)
    if err != nil {
        return fmt.Errorf("http.ReadResponse: %s", err)
    }

    defer func() {
        if resp.Body != nil {
            if err := resp.Body.Close(); err != nil && err != io.ErrUnexpectedEOF {
                fmt.Printf("resp.Body Close error: %s", err)
            }
        }
    }()

    copyHeader(w.Header(), resp.Header)
    w.WriteHeader(resp.StatusCode)

    if _, err := io.Copy(w, resp.Body); err != nil {
        if err == io.ErrUnexpectedEOF {
            fmt.Println("Client closed the connection, couldn't copy response")
        } else {
            fmt.Printf("copy err: %s\n", err)
        }
    }
    return nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if err := s.Handle(w, r); err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
    }
}

func copyHeader(dst, src http.Header) {
    for k, v := range src {
        vv := make([]string, len(v))
        copy(vv, v)
        dst[k] = vv
    }
}

func main() {

    conn, err := net.Dial("tcp", "localhost:3000")

    if err != nil {
        fmt.Errorf("net.Dial(tcp, localhost:3000) %s", err)
    }
    server := &Server{conn}
    log.Fatal(http.ListenAndServe("localhost:8000", server))

}

if the file server distributes a small picture, then everything works correctly if the file server is serving a large video file (1.mp4) then I get the error:

copy err: readfrom tcp 127.0.0.1:8000->127.0.0.1:58964: write tcp 127.0.0.1:8000->127.0.0.1:58964: write: broken pipe

What could be the problem ?

2

There are 2 best solutions below

2
LeGEC On

broken pipe is the golang message linked to errno 32 EPIPE.

When dealing with a TCP connection, it is triggered when your process writes to a connection when the other end has already closed it.


Depening on what you mean by "Client closed the connection", if your intention is to look for an indicator that the outside client that connected to your proxy closed its connection early, then you should be looking for EPIPE errors, not for io.ErrUnexpectedEOF.

Something like :

func isEPIPE(err error) bool {
    var syscallErr syscall.Errno
    b := errors.As(err, &syscallErr) && syscallErr == syscall.EPIPE

    return b
}


...

if isEPIPE(err) {
    fmt.Println("Client closed the connection, couldn't copy response")
} else {
    fmt.Printf("copy err: %s\n", err)
}

io.ErrUnexpectedEOF could probably occur in some situation, but it would be triggered from the connection with your file server, not from the connection initiated by the client.

0
Konstantin On

The browser, when receiving an mp4 file, switches to streaming mode That is, it starts sending http requests with the range header and expects a response with a status of 206

My proxy server always responds with the status 200

 copyHeader(w.Header(), resp.Header)
 w.WriteHeader(resp.StatusCode)

this forces the browser to disconnect the connection