mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-11-24 02:59:13 +08:00
5ce6bbf076
glide has its own requirements. My previous workaround caused me some code checkin errors. Need to fix this.
116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"mime/multipart"
|
|
"net/textproto"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// copied from src/pkg/net/http/fs.go
|
|
|
|
// httpRange specifies the byte range to be sent to the client.
|
|
type httpRange struct {
|
|
start, length int64
|
|
}
|
|
|
|
func (r httpRange) contentRange(size int64) string {
|
|
return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size)
|
|
}
|
|
|
|
func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader {
|
|
return textproto.MIMEHeader{
|
|
"Content-Range": {r.contentRange(size)},
|
|
"Content-Type": {contentType},
|
|
}
|
|
}
|
|
|
|
// parseRange parses a Range header string as per RFC 2616.
|
|
func parseRange(s string, size int64) ([]httpRange, error) {
|
|
if s == "" {
|
|
return nil, nil // header not present
|
|
}
|
|
const b = "bytes="
|
|
if !strings.HasPrefix(s, b) {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
var ranges []httpRange
|
|
for _, ra := range strings.Split(s[len(b):], ",") {
|
|
ra = strings.TrimSpace(ra)
|
|
if ra == "" {
|
|
continue
|
|
}
|
|
i := strings.Index(ra, "-")
|
|
if i < 0 {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
|
|
var r httpRange
|
|
if start == "" {
|
|
// If no start is specified, end specifies the
|
|
// range start relative to the end of the file.
|
|
i, err := strconv.ParseInt(end, 10, 64)
|
|
if err != nil {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
if i > size {
|
|
i = size
|
|
}
|
|
r.start = size - i
|
|
r.length = size - r.start
|
|
} else {
|
|
i, err := strconv.ParseInt(start, 10, 64)
|
|
if err != nil || i > size || i < 0 {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
r.start = i
|
|
if end == "" {
|
|
// If no end is specified, range extends to end of the file.
|
|
r.length = size - r.start
|
|
} else {
|
|
i, err := strconv.ParseInt(end, 10, 64)
|
|
if err != nil || r.start > i {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
if i >= size {
|
|
i = size - 1
|
|
}
|
|
r.length = i - r.start + 1
|
|
}
|
|
}
|
|
ranges = append(ranges, r)
|
|
}
|
|
return ranges, nil
|
|
}
|
|
|
|
// countingWriter counts how many bytes have been written to it.
|
|
type countingWriter int64
|
|
|
|
func (w *countingWriter) Write(p []byte) (n int, err error) {
|
|
*w += countingWriter(len(p))
|
|
return len(p), nil
|
|
}
|
|
|
|
// rangesMIMESize returns the number of bytes it takes to encode the
|
|
// provided ranges as a multipart response.
|
|
func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) {
|
|
var w countingWriter
|
|
mw := multipart.NewWriter(&w)
|
|
for _, ra := range ranges {
|
|
mw.CreatePart(ra.mimeHeader(contentType, contentSize))
|
|
encSize += ra.length
|
|
}
|
|
mw.Close()
|
|
encSize += int64(w)
|
|
return
|
|
}
|
|
|
|
func sumRangesSize(ranges []httpRange) (size int64) {
|
|
for _, ra := range ranges {
|
|
size += ra.length
|
|
}
|
|
return
|
|
}
|