github.com/searKing/golang/go@v1.2.117/net/http/content_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 "fmt" 10 "net/http" 11 "strconv" 12 "strings" 13 ) 14 15 // errNoOverlap is returned by serveContent's parseContentRange if first-byte-pos of 16 // all of the byte-range-spec values is greater than the content size. 17 var errNoOverlap = errors.New("invalid range: failed to overlap") 18 19 // httpContentRange specifies the byte range to be sent to the client. 20 type httpContentRange struct { 21 firstBytePos, lastBytePos, completeLength int64 22 } 23 24 func (r httpContentRange) String() string { 25 var b strings.Builder 26 b.WriteString("bytes ") 27 if r.firstBytePos == -1 && r.lastBytePos == -1 { 28 b.WriteString("*") 29 } else { 30 if r.firstBytePos == -1 { 31 b.WriteString("*") 32 } else { 33 b.WriteString(fmt.Sprintf("%d", r.firstBytePos)) 34 } 35 b.WriteString("-") 36 if r.lastBytePos == -1 { 37 b.WriteString("*") 38 } else { 39 b.WriteString(fmt.Sprintf("%d", r.lastBytePos)) 40 } 41 } 42 b.WriteString("/") 43 44 if r.completeLength < 0 { 45 b.WriteString("*") 46 } else { 47 b.WriteString(fmt.Sprintf("%d", r.completeLength)) 48 } 49 return b.String() 50 } 51 52 func parseContentRanges(s []string) ([]httpContentRange, error) { 53 var ranges []*httpContentRange 54 55 for _, ra := range s { 56 ra = strings.TrimSpace(ra) 57 if ra == "" { 58 continue 59 } 60 r, err := parseContentRange(ra) 61 if err != nil { 62 return nil, err 63 } 64 ranges = append(ranges, r) 65 } 66 67 var size int64 = -1 68 for _, ra := range ranges { 69 if size != -1 && ra.completeLength != -1 && size != ra.completeLength { 70 return nil, errors.New("invalid range") 71 } 72 if ra.completeLength >= 0 { 73 size = ra.completeLength 74 } 75 } 76 77 for _, ra := range ranges { 78 ra.completeLength = size 79 if ra.firstBytePos < 0 { 80 ra.firstBytePos = 0 81 } 82 if ra.lastBytePos < 0 { 83 if size < 0 { 84 return nil, errors.New("invalid range") 85 } 86 ra.lastBytePos = size 87 } 88 } 89 90 var totalSize int64 91 for _, ra := range ranges { 92 totalSize += ra.lastBytePos - ra.firstBytePos + 1 93 } 94 95 if size >= 0 && size != totalSize { 96 return nil, errors.New("invalid range") 97 } 98 99 var outRanges []httpContentRange 100 for _, ra := range ranges { 101 ra.completeLength = totalSize 102 outRanges = append(outRanges, *ra) 103 } 104 105 return outRanges, nil 106 } 107 108 // parseContentRange parses a Range header string as per RFC 7233. 109 // errNoOverlap is returned if none of the ranges overlap. 110 func parseContentRange(s string) (contentRange *httpContentRange, err error) { 111 // bytes 0-499/1234 112 if s == "" { 113 return nil, nil // header not present 114 } 115 const b = "bytes " 116 if !strings.HasPrefix(s, b) { 117 return nil, errors.New("invalid range") 118 } 119 120 ra := strings.TrimSpace(s[len(b):]) 121 if ra == "" { 122 return nil, nil 123 } 124 125 i := strings.Index(ra, "/") 126 if i < 0 { 127 return nil, errors.New("invalid range") 128 } 129 byteRange := strings.TrimSpace(ra[:i]) 130 completeLength := strings.TrimSpace(ra[i+1:]) 131 132 if byteRange == "*" && completeLength == "*" { 133 return nil, errors.New("invalid range") 134 } 135 if byteRange == "" && completeLength == "" { 136 return nil, errors.New("invalid range") 137 } 138 if byteRange == "*" && completeLength == "" { 139 return nil, errors.New("invalid range") 140 } 141 if byteRange == "" && completeLength == "*" { 142 return nil, errors.New("invalid range") 143 } 144 145 var r = httpContentRange{ 146 firstBytePos: -1, 147 lastBytePos: -1, 148 completeLength: -1, 149 } 150 if byteRange != "*" { 151 i := strings.Index(byteRange, "-") 152 if i < 0 { 153 return nil, errors.New("invalid range") 154 } 155 firstBytePos := strings.TrimSpace(byteRange[:i]) 156 lastBytePos := strings.TrimSpace(byteRange[i+1:]) 157 if firstBytePos != "" { 158 i, err := strconv.ParseInt(firstBytePos, 10, 64) 159 if err != nil { 160 return nil, errors.New("invalid range") 161 } 162 r.firstBytePos = i 163 } 164 if lastBytePos != "" { 165 i, err := strconv.ParseInt(lastBytePos, 10, 64) 166 if err != nil { 167 return nil, errors.New("invalid range") 168 } 169 r.lastBytePos = i 170 } 171 } 172 173 if completeLength != "*" { 174 i, err := strconv.ParseInt(completeLength, 10, 64) 175 if err != nil { 176 return nil, errors.New("invalid range") 177 } 178 r.completeLength = i 179 } 180 181 if r.firstBytePos < 0 && r.lastBytePos < 0 && r.completeLength < 0 { 182 return nil, errors.New("invalid range") 183 } 184 185 if r.firstBytePos >= 0 && r.lastBytePos >= 0 && r.firstBytePos > r.lastBytePos { 186 return nil, errors.New("invalid range") 187 } 188 189 if r.firstBytePos >= 0 && r.lastBytePos >= 0 && r.completeLength >= 0 && (r.lastBytePos-r.firstBytePos+1 < r.completeLength) { 190 // The specified ranges did not overlap with the content. 191 return nil, errNoOverlap 192 } 193 return &r, nil 194 } 195 196 // countingWriter counts how many bytes have been written to it. 197 type countingWriter int64 198 199 func (w *countingWriter) Write(p []byte) (n int, err error) { 200 *w += countingWriter(len(p)) 201 return len(p), nil 202 } 203 204 func writeContentRanges(w http.ResponseWriter, ranges []httpContentRange) { 205 for _, ra := range ranges { 206 w.Header().Add("Content-Range", ra.String()) 207 } 208 }