Skip to content

Instantly share code, notes, and snippets.

@mmichaelb
Last active October 5, 2019 14:56
Show Gist options
  • Save mmichaelb/56dc7e1b755f039e9324db10bc8157e0 to your computer and use it in GitHub Desktop.
Save mmichaelb/56dc7e1b755f039e9324db10bc8157e0 to your computer and use it in GitHub Desktop.
Simple method to calculate the Multipart file length of a multipart upload with a single file.
package multipartdetection
import (
"bufio"
"bytes"
"errors"
)
const (
// MultipartBodySniffLength declares the amount of bytes to read from the multipart body in
// order to detect the file length.
MultipartBodySniffLength = 256
)
var (
// ErrBoundaryInvalid indicates that the boundary is invalid or not even set. It may also be
// returned if the boundary is too long (more than the 70 allowed characters, see RFC 2046
// http://www.ietf.org/rfc/rfc2046.txt).
ErrBoundaryInvalid = errors.New("boundary is too long")
// ErrUnknownCharacter indicates that a different character was expected so therefore the
// multipart body is invalid.
ErrUnknownCharacter = errors.New("expected different character")
// ErrSniffSizeExceeded indicates that the sniff (length: MultipartBodySniffLength) was not enough
// to scan for the whole multipart boundary prefix.
ErrSniffSizeExceeded = errors.New("the maximum size of the multipart body sniff was exceeded")
// ErrInvalidContentLength indicates that the provided content length is wrong because of the
// calculated file length (assuming the multipart body is correct).
ErrInvalidContentLength = errors.New("invalid content length provided")
)
// CalculateSingleMultipartFileLength calculates and in a successful case returns the real file
// length. Please note that this method only works if the multipart request contains one file.
func CalculateSingleMultipartFileLength(boundary string, bodyBuffer *bufio.Reader, contentLength int64) (fileLength int64, err error) {
if len(boundary) > 70 {
return 0, ErrBoundaryInvalid
}
sniff, _ := bodyBuffer.Peek(MultipartBodySniffLength)
err = nil
byteBoundary := []byte(boundary)
var i int
// iterate through the body sniff
for i = 0; i < len(sniff); i++ {
c := rune(sniff[i])
var ok bool
/* check for dash prefix (--) */ if i <= 1 {
if c != '-' {
return 0, ErrUnknownCharacter
}
continue
} else /* check boundary*/ if i >= 2 && i < 2+len(byteBoundary) {
i, ok = validateBoundary(i, byteBoundary, sniff)
if !ok {
return 0, ErrBoundaryInvalid
}
continue
} else /* check for two consecutive line separators indicating the beginning of the real body */ {
if i, ok = validateLineSeparator(i, sniff); !ok {
continue
}
// increment i to allow the next line separator check
i++
if i, ok = validateLineSeparator(i, sniff); ok {
goto lengthCalcuation
}
}
}
return 0, ErrSniffSizeExceeded
lengthCalcuation:
// subtract the calculated prefix length (added by one because array start at 0), two preceding
// and ending line separators as well as the boundary length and the line separators from the
// total content length
fileLength = contentLength - (int64(i + 1 + 4 + len(byteBoundary) + 2 + 2))
if fileLength < 0 {
return 0, ErrInvalidContentLength
}
return
}
func validateLineSeparator(i int, sniff []byte) (int, bool) {
if len(sniff) < i+2 {
return i, false
}
if sniff[i] != '\r' {
return i, false
}
if sniff[i+1] != '\n' {
return i, false
}
return i + 1, true
}
func validateBoundary(i int, byteBoundary []byte, sniff []byte) (int, bool) {
if len(sniff) <= len(byteBoundary)+i {
return 0, false
}
if bytes.Equal(byteBoundary, sniff[i:i+len(byteBoundary)]) {
return i + len(byteBoundary) - 1, true
}
return 0, false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment