helpers/rangeheader.go (51 lines of code) (raw):
package helpers
import (
"errors"
"net/http"
"regexp"
"strconv"
"strings"
)
type RangeHeader struct {
Start int64
End int64
TotalSize int64
}
/**
returns true if the given range has a start of zero
*/
func (h *RangeHeader) IsFirst() bool {
return h.Start == 0
}
/**
returns true if the given range ends at the total size
*/
func (h *RangeHeader) IsLast() bool {
return h.End >= h.TotalSize
}
/**
returns true if this is the one and only chunk
*/
func (h *RangeHeader) IsComplete() bool {
return h.IsFirst() && h.IsLast()
}
func ParseRangeHeader(headerContent string) (*RangeHeader, error) {
xtractor := regexp.MustCompile("^(\\w+)=(\\d+)-(\\d+)/(\\d+)$")
matches := xtractor.FindAllStringSubmatch(headerContent, -1)
if matches == nil {
return nil, errors.New("range header malformed")
}
if strings.ToLower(matches[0][1]) != "bytes" {
return nil, errors.New("need ranges in bytes")
}
startNum, startParseErr := strconv.ParseInt(matches[0][2], 10, 64)
endNum, endParseErr := strconv.ParseInt(matches[0][3], 10, 64)
totalNum, totalParseErr := strconv.ParseInt(matches[0][4], 10, 64)
if startParseErr != nil || endParseErr != nil || totalParseErr != nil {
return nil, errors.New("need all range parameters as numbers")
}
return &RangeHeader{
Start: startNum,
End: endNum,
TotalSize: totalNum,
}, nil
}
/**
extracts any Range header, if present.
if no range header is present, (nil, nil) is returned
if there was an error interpreting it, an error is returned only
otherwise a pointer to a new RangeHeader object is returned
*/
func ExtractRange(h *http.Request) (*RangeHeader, error) {
rangeHeaderRaw := h.Header.Get("Range")
if rangeHeaderRaw == "" {
return nil, nil
} else {
return ParseRangeHeader(rangeHeaderRaw)
}
}