107 lines
1.9 KiB
Go
107 lines
1.9 KiB
Go
|
package httptoo
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type BytesContentRange struct {
|
||
|
First, Last, Length int64
|
||
|
}
|
||
|
|
||
|
type BytesRange struct {
|
||
|
First, Last int64
|
||
|
}
|
||
|
|
||
|
func (me BytesRange) String() string {
|
||
|
if me.Last == math.MaxInt64 {
|
||
|
return fmt.Sprintf("bytes=%d-", me.First)
|
||
|
}
|
||
|
return fmt.Sprintf("bytes=%d-%d", me.First, me.Last)
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
httpBytesRangeRegexp = regexp.MustCompile(`bytes[ =](\d+)-(\d*)`)
|
||
|
)
|
||
|
|
||
|
func ParseBytesRange(s string) (ret BytesRange, ok bool) {
|
||
|
ss := httpBytesRangeRegexp.FindStringSubmatch(s)
|
||
|
if ss == nil {
|
||
|
return
|
||
|
}
|
||
|
var err error
|
||
|
ret.First, err = strconv.ParseInt(ss[1], 10, 64)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if ss[2] == "" {
|
||
|
ret.Last = math.MaxInt64
|
||
|
} else {
|
||
|
ret.Last, err = strconv.ParseInt(ss[2], 10, 64)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
ok = true
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func parseUnitRanges(s string) (unit, ranges string) {
|
||
|
s = strings.TrimSpace(s)
|
||
|
i := strings.IndexAny(s, " =")
|
||
|
if i == -1 {
|
||
|
return
|
||
|
}
|
||
|
unit = s[:i]
|
||
|
ranges = s[i+1:]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func parseFirstLast(s string) (first, last int64) {
|
||
|
ss := strings.SplitN(s, "-", 2)
|
||
|
first, err := strconv.ParseInt(ss[0], 10, 64)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
last, err = strconv.ParseInt(ss[1], 10, 64)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func parseContentRange(s string) (ret BytesContentRange) {
|
||
|
ss := strings.SplitN(s, "/", 2)
|
||
|
firstLast := strings.TrimSpace(ss[0])
|
||
|
if firstLast == "*" {
|
||
|
ret.First = -1
|
||
|
ret.Last = -1
|
||
|
} else {
|
||
|
ret.First, ret.Last = parseFirstLast(firstLast)
|
||
|
}
|
||
|
il := strings.TrimSpace(ss[1])
|
||
|
if il == "*" {
|
||
|
ret.Length = -1
|
||
|
} else {
|
||
|
var err error
|
||
|
ret.Length, err = strconv.ParseInt(il, 10, 64)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func ParseBytesContentRange(s string) (ret BytesContentRange, ok bool) {
|
||
|
unit, ranges := parseUnitRanges(s)
|
||
|
if unit != "bytes" {
|
||
|
return
|
||
|
}
|
||
|
ret = parseContentRange(ranges)
|
||
|
ok = true
|
||
|
return
|
||
|
}
|