Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang file upload: close connection if file is too large

Tags:

http

go

upload

I want to allow uploading files. Go is being used server-side to handle requests. I would like to send a response "File too large" whenever the file they're trying to upload is too large. I would like to do so, before the entire file is uploaded (bandwidth).

I am using the following snippet, but it's only sending a response after the client is done uploading. It saves a 5 kB file.

const MaxFileSize = 5 * 1024
// This feels like a bad hack...
if r.ContentLength > MaxFileSize {
    if flusher, ok := w.(http.Flusher); ok {
        response := []byte("Request too large")
        w.Header().Set("Connection", "close")
        w.Header().Set("Content-Length", fmt.Sprintf("%d", len(response)))
        w.WriteHeader(http.StatusExpectationFailed)
        w.Write(response)
        flusher.Flush()
    }
    conn, _, _ :=  w.(http.Hijacker).Hijack()
    conn.Close()
    return
}

r.Body = http.MaxBytesReader(w, r.Body, MaxFileSize)

err := r.ParseMultipartForm(1024)
if err != nil {
    w.Write([]byte("File too large"));
    return
}

file, header, err := r.FormFile("file")
if err != nil {
    panic(err)
}

dst, err := os.Create("upload/" + header.Filename)
defer dst.Close()
if err != nil { 
    panic(err)
}

written, err := io.Copy(dst, io.LimitReader(file, MaxFileSize))
if err != nil {
    panic(err)
}

if written == MaxFileSize {
    w.Write([]byte("File too large"))
    return
}
w.Write([]byte("Success..."))
like image 411
Etienne Bruines Avatar asked Oct 15 '14 21:10

Etienne Bruines


1 Answers

Most clients do not read the response until done writing the request. Responding with an error from the server will not cause these clients to stop writing.

The net/http server supports the 100 continue status. To use this feature, the server application should respond with an error before reading the request body:

func handler(w http.ResponseWriter, r *http.Request) {
  if r.ContentLength > MaxFileSize {
     http.Error(w, "request too large", http.StatusExpectationFailed)
     return
  }
  r.Body = http.MaxBytesReader(w, r.Body, MaxFileSize)
  err := r.ParseMultipartForm(1024)

  // ... continue as before

If the client sent the "Expect: 100-continue" header, then the client should wait for the 100 continue status before writing the request body. The net/http server automatically sends the 100 continue status when the server application reads the request body. The server can stop the client from writing the request body by replying with an error before reading the request.

The net/http client does not support the 100 continue status.

If the client did not send the expect header and the server application returns from the request handler without reading the complete request body, then the net/http server reads and discards up to 256 << 10 bytes of the request body. The server will closes the connection if the entire request body was not read.

like image 121
Bayta Darell Avatar answered Nov 03 '22 00:11

Bayta Darell