Last active
October 5, 2019 14:56
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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