github.com/searKing/golang/go@v1.2.117/net/http/range.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package http 6 7 import ( 8 "errors" 9 "net/textproto" 10 "strconv" 11 "strings" 12 ) 13 14 // httpRange specifies the byte range to be sent to the client. 15 type httpRange struct { 16 start, length int64 17 } 18 19 func (r httpRange) contentRange(size int64) string { 20 cr := &httpContentRange{ 21 firstBytePos: r.start, 22 lastBytePos: r.start + r.length - 1, 23 completeLength: size, 24 } 25 return cr.String() 26 } 27 28 func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader { 29 return textproto.MIMEHeader{ 30 "Content-Range": {r.contentRange(size)}, 31 "Content-Type": {contentType}, 32 } 33 } 34 35 // parseRange parses a Range header string as per RFC 7233. 36 // errNoOverlap is returned if none of the ranges overlap. 37 func parseRange(s string, size int64) ([]httpRange, error) { 38 if s == "" { 39 return nil, nil // header not present 40 } 41 const b = "bytes=" 42 if !strings.HasPrefix(s, b) { 43 return nil, errors.New("invalid range") 44 } 45 var ranges []httpRange 46 noOverlap := false 47 for _, ra := range strings.Split(s[len(b):], ",") { 48 ra = strings.TrimSpace(ra) 49 if ra == "" { 50 continue 51 } 52 i := strings.Index(ra, "-") 53 if i < 0 { 54 return nil, errors.New("invalid range") 55 } 56 start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:]) 57 var r httpRange 58 if start == "" { 59 // If no start is specified, end specifies the 60 // range start relative to the end of the file. 61 i, err := strconv.ParseInt(end, 10, 64) 62 if err != nil { 63 return nil, errors.New("invalid range") 64 } 65 if i > size { 66 i = size 67 } 68 r.start = size - i 69 r.length = size - r.start 70 } else { 71 i, err := strconv.ParseInt(start, 10, 64) 72 if err != nil || i < 0 { 73 return nil, errors.New("invalid range") 74 } 75 if i >= size { 76 // If the range begins after the size of the content, 77 // then it does not overlap. 78 noOverlap = true 79 continue 80 } 81 r.start = i 82 if end == "" { 83 // If no end is specified, range extends to end of the file. 84 r.length = size - r.start 85 } else { 86 i, err := strconv.ParseInt(end, 10, 64) 87 if err != nil || r.start > i { 88 return nil, errors.New("invalid range") 89 } 90 if i >= size { 91 i = size - 1 92 } 93 r.length = i - r.start + 1 94 } 95 } 96 ranges = append(ranges, r) 97 } 98 if noOverlap && len(ranges) == 0 { 99 // The specified ranges did not overlap with the content. 100 return nil, errNoOverlap 101 } 102 return ranges, nil 103 }